P2690 接苹果

\(problem\)

看到这题 好几种方法
1.搜索 大概有一部分分
2.记忆化搜索 预计100pts
3.DP 预计100pts
4.滚动数组优化DP 预计100pts
---------------------------------------------------------------------------------------

1.不带记忆化的搜索妥妥的超时
2.记忆化搜索? 三个维度? 内存太大? 时间还是\(O(玄学)\)的。
3.DP 能过 但是可以适当优化
4.滚动数组优化的DP
----------------------------------------------------------------------------------------

主要是DP
搜索太过于单调了。代码也比较好写。
可能还需要剪枝。那么DP?

不难想。 每次有两种。 一种是走。另一种是在原地。
\(dp[i][j]\)表示奶牛在第\(i\)分钟内移动\(j\)次能够接到的最多苹果
所以可以推出来动态转移方程
\(dp[i][j]=max(dp[i-1][j],dp[i-1][j-1])\)
上面的方程表示上一步的状态可能是不动的 可能是动的。
这是一种推的方式 还有一种推的方式是反过来的。
那就不说那么多了 往下。
同时如果\(a[i]==j%2+1\)即奶牛在这一分钟可以见到苹果 那么把\(dp[i][j]++\)
因为初始位置是1 所以\(j%2+1\)是奶牛当前位置。 如果当前位置=苹果位置 那么就可以增加苹果量。
不难得出这一段代码

    for(register int i=1;i<=T;i++){
        for(register int j=0;j<=T and j<=w;j++){
            if(!j) dp[i][j]=dp[i-1][j];
            else dp[i][j]=max(dp[i-1][j],dp[i-1][j-1]);
            if(a[i]==j%2+1) dp[i][j]++;
        }
    }

所以求完之后 再根据题目要求 求出来最多的苹果数量。
显然。\(DP[T][w]\)不一定是最大值。
因为有时候不移动比移动要好。
那么往下走。

得出代码

    LL ans=-0x7f;
    for(register int i=0;i<=w;i++) ans=max(ans,dp[T][i]);
    cout << ans << endl;

那么基本程序就完成了。

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
inline LL In() { LL res(0),f(1); register char c ;
    while(isspace(c=getchar())) ; c == '-'? f = -1 , c = getchar() : f = 1 ;
    while(res = (res << 1) + (res << 3) + (c & 15) , isdigit(c=getchar())) ; return res * f ;
}
int T;
int w;
int a[1<<10];
LL dp[1<<10][1<<5];
signed main () {
    T=In(); w=In();
    for(register int i=1;i<=T;i++) a[i]=In();
    for(register int i=1;i<=T;i++){
        for(register int j=0;j<=T and j<=w;j++){
            if(!j) dp[i][j]=dp[i-1][j];
            else dp[i][j]=max(dp[i-1][j],dp[i-1][j-1]);
            if(a[i]==j%2+1) dp[i][j]++;
        }
    }
    LL ans=-0x7f;
    for(register int i=0;i<=w;i++) ans=max(ans,dp[T][i]);
    cout << ans << endl;
    return 0;
}

如果对滚动数组有兴趣的 往下看。

滚动数组,顾名思义。就是滚动的数组。反复利用同一个数组的同一个下标变量。
因为有时候你只需要上一个状态而不是要许许多多的状态。
所以如果需要上一个状态的话 可以进行\(i&1\)的操作(即i%2)
这样子会节省内存。滚动数组很有用的

LL dp[1<<10][1<<5];//无滚动数组的数组大小
LL dp[2][1<<5];//滚动数组的数组大小

因为\(线性DP\)以及\(区间DP\)都可以用这个来优化内存。
比如说 刚才 那一段\(DP\)的过程 可以转化为 ↓

    for(register int i=1;i<=T;i++){
        for(register int j=0;j<=T and j<=w;j++){
            if(!j) dp[i&1][j]=dp[(i-1)&1][j];
            else dp[i&1][j]=max(dp[(i-1)&1][j],dp[(i-1)&1][j-1]);
            if(a[i]==j%2+1) dp[i&1][j]++;
        }
    }

然后也求出来了。
不过求最大值需要注意一点。

不是dp[T][0~w]!!!!(敲黑板)

    LL ans=-0x7f;
    T&=1;
    for(register int i=0;i<=w;i++) ans=max(ans,dp[T][i]);
    cout << ans << endl;

下面是滚动数组优化的代码。

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
inline LL In() { LL res(0),f(1); register char c ;
    while(isspace(c=getchar())) ; c == '-'? f = -1 , c = getchar() : f = 1 ;
    while(res = (res << 1) + (res << 3) + (c & 15) , isdigit(c=getchar())) ; return res * f ;
}
int T;
int w;
int a[1<<10];
LL dp[2][1<<5];
signed main () {
    T=In(); w=In();
    for(register int i=1;i<=T;i++) a[i]=In();
    for(register int i=1;i<=T;i++){
        for(register int j=0;j<=T and j<=w;j++){
            if(!j) dp[i&1][j]=dp[(i-1)&1][j];
            else dp[i&1][j]=max(dp[(i-1)&1][j],dp[(i-1)&1][j-1]);
            if(a[i]==j%2+1) dp[i&1][j]++;
        }
    }
    LL ans=-0x7f;
    T&=1;
    for(register int i=0;i<=w;i++) ans=max(ans,dp[T][i]);
    cout << ans << endl;
    return 0;
}

P2690 接苹果

上一篇:Spring Boot 2 - 使用CommandLineRunner与ApplicationRun


下一篇:java面试总躲不过的并发(二):volatile原理 + happens-before原则