算法竞赛进阶指南,347页,线性dp
本题要点:
1、状态表示:
f[a][b][c][d]:表示你出了a张爬行牌1,b张爬行牌2,c张爬行牌3,d张爬行牌4时的得分
2、转态转移方程:
假设牌1, 牌2,牌3,牌4分别出现了 a, b, c, d次。
当前的坐标是x。 有x往回走1步,对应的最大得分是 f[a - 1][b][c][d],
同理, 有x往回走2步,对应的最大得分是 f[a][b - 1][c][d],
有x往回走3步,对应的最大得分是 f[a][b][c - 1][d],
有x往回走3步,对应的最大得分是 f[a][b][c][d - 1],
以上4个取一个最大值,和 score[x] 相加,就是 f[a][b][c][d] 的取值。
#include <cstdio>
#include <cstring>
#include <iostream>
using namespace std;
const int MaxN = 360;
int score[MaxN];
int card[5]; //a[1] 表示牌数等于1 的牌子个数
int f[41][41][41][41];
//f[a][b][c][d]:表示你出了a张爬行牌1,b张爬行牌2,c张爬行牌3,d张爬行牌4时的得分
int n, m;
void sovle()
{
int x = 0; //表示当前所在的坐标
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)
{
x = 1 + a + b * 2 + c * 3 + d * 4; //算出当前所在的坐标
f[a][b][c][d] = score[x];
int pre = 0; //向前走1,2, 3, 4 步得到的最大值
if(a > 0)
pre = max(pre, f[a - 1][b][c][d]);
if(b > 0)
pre = max(pre, f[a][b - 1][c][d]);
if(c > 0)
pre = max(pre, f[a][b][c - 1][d]);
if(d > 0)
pre = max(pre, f[a][b][c][d - 1]);
f[a][b][c][d] += pre;
}
}
}
}
printf("%d\n", f[card[1]][card[2]][card[3]][card[4]]);
}
int main()
{
scanf("%d%d", &n, &m);
for(int i = 1; i <= n; ++i)
{
scanf("%d", &score[i]);
}
int num;
for(int i = 0; i < m; ++i)
{
scanf("%d", &num);
card[num]++;
}
sovle();
return 0;
}
/*
9 5
6 10 14 2 8 8 18 5 17
1 3 1 2 1
*/
/*
73
*/