[NOIP2010] 乌龟棋
小明过生日的时候,爸爸送给他一副乌龟棋当作礼物。
乌龟棋的棋盘是一行N个格子,每个格子上一个分数(非负整数)。棋盘第1格是唯一的起点,第N格是终点,游戏要求玩家控制一个乌龟棋子从起点出发走到终点。
乌龟棋中M张爬行卡片,分成4种不同的类型(M张卡片中不一定包含所有4种类型的卡片,见样例),每种类型的卡片上分别标有1、2、3、4四个数字之一,表示使用这种卡片后,乌龟棋子将向前爬行相应的格子数。游戏中,玩家每次需要从所有的爬行卡片中选择一张之前没有使用过的爬行卡片,控制乌龟棋子前进相应的格子数,每张卡片只能使用一次。
游戏中,乌龟棋子自动获得起点格子的分数,并且在后续的爬行中每到达一个格子,就得到该格子相应的分数。玩家最终游戏得分就是乌龟棋子从起点到终点过程中到过的所有格子的分数总和。
很明显,用不同的爬行卡片使用顺序会使得最终游戏的得分不同,小明想要找到一种卡片使用顺序使得最终游戏得分最多。
现在,告诉你棋盘上每个格子的分数和所有的爬行卡片,你能告诉小明,他最多能得到多少分吗?
多阶段决策寻找最优方案,满足DP诸多性质,上转移方程水之。
dp[x][i][j][k][l]表示使用走到x位置同时使用了i张1卡,j张2卡,k张3卡,l张4卡时所能得到的最大分数。题目数据量虽然非常小,使用五维数组仍会MLE。于是对状态定义进行优化:位置x可以由1234卡使用的张数推出,便减少了一维,空间使用暴减350倍,你值得拥有
/*状态定义:dp[i][j][k][l]表示使用i张1卡,j张2卡,k张3卡,l张4卡时所能得到的最大分数。
转移方程:dp[i][j][k][l]=max{dp[i-1][j][k][l]+w[d-1],dp[i][j-1][k][l]+w[d-2],dp[i][j][k-1][l]+w[d-3],dp[i][j][k][l-1]+w[d-4]}
其中d=i+j*2+k*3+l*4;且i,j,k,l大于0时进行转移
边界:dp[0][0][0][0]=w[0];dp[num_of_1][num_of_2][num_of_3][num_of_4]为最大分值。*/
#include<cstdio>
#include<iostream>
#include<algorithm>
#define COGS
using namespace std;
int n,m;
int dp[40][40][40][40];
int w[400];
int e[5];
void Init(){
int a;
scanf("%d%d",&n,&m);
for(int i=0;i<n;i++)scanf("%d",&w[i]);
for(int i=0;i<m;i++){
scanf("%d",&a);
e[a]++;
}
}
void Work(){
dp[0][0][0][0]=w[0];
for(int i=0;i<=e[1];i++)
for(int j=0;j<=e[2];j++)
for(int k=0;k<=e[3];k++)
for(int l=0;l<=e[4];l++){
int d=i+j*2+k*3+l*4;
//dp[i][j][k][l]=max(max(dp[i-1][j][k][l]+w[d-1],dp[i][j-1][k][l]+w[d-2]),max(dp[i][j][k-1][l]+w[d-3],dp[i][j][k][l-1]+w[d-4]));
if(i)dp[i][j][k][l]=max(dp[i][j][k][l],dp[i-1][j][k][l]+w[d]);
if(j)dp[i][j][k][l]=max(dp[i][j][k][l],dp[i][j-1][k][l]+w[d]);
if(k)dp[i][j][k][l]=max(dp[i][j][k][l],dp[i][j][k-1][l]+w[d]);
if(l)dp[i][j][k][l]=max(dp[i][j][k][l],dp[i][j][k][l-1]+w[d]);
}
printf("%d",dp[e[1]][e[2]][e[3]][e[4]]);
}
int main()
{
#ifdef COGS
string FileName="tortoise";
freopen((FileName+".in").c_str(),"r",stdin);
freopen((FileName+".out").c_str(),"w",stdout);
#endif
Init();
Work();
}