首先想到状态:f[x][a][b][c][d],表示已用a张一步卡、b张两步卡、c张三步卡、d张四步卡、走到第x格的最大得分。但五维dp容易超空间又容易超时,所以试着对状态进行优化。
我们发现,x用于计算每格的得分;而已知a, b, c, d时,x可求得,所以可以省掉第一维状态。 x = 1 + 1*a + 2*b + 3*c + 4*d (注意此处的1)
状态:f[a][b][c][d] 已用a张一步卡、b张两步卡、c张三步卡、d张四步卡的最大得分
因为输入数据保证到终点时一定用完四种卡片,所以最终状态一定是f[card[1]][card[2]][card[3]][card[4]]
#include<iostream>
#include<cstdio>
#include<algorithm>
#define dp0 dp[a][b][c][d]
using namespace std;
int n,m,card[5],k[400],dp[41][41][41][41];
int main()
{
scanf("%d%d",&n,&m);
for (int i=1; i<=n; i++) scanf("%d",&k[i]);
for (int i=1; i<=m; i++) {
int p;
scanf("%d",&p);
card[p]++;
}
for (int a=0; a<=card[1]; a++)
for (int b=0; b<=card[2]; b++)
for (int c=0; c<=card[3]; c++)
for (int d=0; d<=card[4]; d++) {
dp0 = 0;
if (a>0) dp0 = max(dp0,dp[(a-1)][b][c][d]); //注意判断a>0!
if (b>0) dp0 = max(dp0,dp[a][(b-1)][c][d]);
if (c>0) dp0 = max(dp0,dp[a][b][(c-1)][d]);
if (d>0) dp0 = max(dp0,dp[a][b][c][(d-1)]);
dp0 += k[1+a+2*b+3*c+4*d];
}
printf("%d",dp[card[1]][card[2]][card[3]][card[4]]);
return 0;
}
为省空间,可采用循环队列。
因为dp[a][b][c][d]只与dp[a/a-1][b/b-1][c/c-1][d/d-1]有关,所以开数组dp[2][2][2][2]。
循环中dp[a%2][b%2][c%2][d%2]