luogu P1541 乌龟棋
动态规划问题,讲一下状态设计的要领。
状态设计
f[a1][a2][a3][a4]表示1、2、3、4这四种牌分别用了a1 a2 a3 a4张之后能达到的最大路径。
状态转移
f[a1+1][a2][a3][a4] = max(f[a1+1][a2][a3][a4],f[a1][a2][a3][a4] + a[x+1]);
f[a1][a2+1][a3][a4] = max(f[a1][a2+1][a3][a4],f[a1][a2][a3][a4] + a[x+2])
f[a1][a2][a3+1][a4] = max(f[a1][a2][a3+1][a4],f[a1][a2][a3][a4] + a[x+3])
f[a1][a2][a3][a4+1] = max(f[a1][a2][a3][a4+1],f[a1][a2][a3][a4] + a[x+4])
初始化
f[0][0][0][0] = a[1]也就是每张牌都没用,在起点的时候的分值达到了a[1]。
状态设计过程
首先看到两点:变量和范围。动态规划的第一步就是把所有可能影响结果的变量都单独放一维。然后再考虑是否能缩减、简化。
这时候我们得到的方程是: f[i][a1][a2][a3][a4]表示走到i的时候,1、2、3、4分别用了a1、a2、a3、a4张牌,能够得到的最大值。那么我们的转移就是:
f[i+1][a1+1][a2][a3][a4] = max(f[i+1][a1+1][a2][a3][a4],f[i][a1][a2][a3][a4] + a[i+1]);
f[i+2][a1][a2+1][a3][a4] = max(f[i+2][a1][a2+1][a3][a4],f[i][a1][a2][a3][a4] + a[i+2]);
f[i+3][a1][a2][a3+1][a4] = max(f[i+3][a1][a2][a3+1][a4],f[i][a1][a2][a3][a4] + a[i+3]);
f[i+4][a1][a2][a3][a4+1] = max(f[i+4][a1][a2][a3][a4+1],f[i][a1][a2][a3][a4] + a[i+4]);
根据题目给的范围我们可以很简单地知道复杂度:(O(MNprod_{i = 1}^{M}b_i)),空间直接整到了(350 imes 41^4 imes 4B= 989,016,350B = 943.1994915MB),很显然超出了内存限制,考虑缩减。
联想到我们把01背包和无限背包降维打击成1维,利用的是他循环覆盖的特点,我们也可以把f的第一维去掉。这样复杂度就降下来了好多,时间也允许了。
代码
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
int n,m;
int a[400],k[5];
int f[41][41][41][41];
int main(){
cin >> n >> m;
for(int i = 1;i <= n; i++){
cin >> a[i];
}
for(int i = 1;i <= m; i++){
int t;
cin >> t;
k[t]++;
}
// for(int i = 1;i <= 4; i++) cout << k[i] << ' ';
f[0][0][0][0] = a[1];
for(int a1 = 0;a1 <= k[1]; a1++){
for(int a2 = 0;a2 <= k[2]; a2++){
for(int a3 = 0;a3 <= k[3]; a3++){
for(int a4 = 0;a4 <= k[4]; a4++){
int x = a1 + a2 * 2 + a3 * 3 + a4 * 4 + 1;
if(a1+1 <= k[1]) f[a1+1][a2][a3][a4] = max(f[a1+1][a2][a3][a4],f[a1][a2][a3][a4] + a[x+1]);
if(a2+1 <= k[2]) f[a1][a2+1][a3][a4] = max(f[a1][a2+1][a3][a4],f[a1][a2][a3][a4] + a[x+2]);
if(a3+1 <= k[3]) f[a1][a2][a3+1][a4] = max(f[a1][a2][a3+1][a4],f[a1][a2][a3][a4] + a[x+3]);
if(a4+1 <= k[4]) f[a1][a2][a3][a4+1] = max(f[a1][a2][a3][a4+1],f[a1][a2][a3][a4] + a[x+4]);
}
}
}
}
cout << f[k[1]][k[2]][k[3]][k[4]] << endl;
return 0;
}