NOIP2010动态规划题
题目
小明过生日的时候,爸爸送给他一副乌龟棋当作礼物。 乌龟棋的棋盘是一行N个格子,每个格子上一个分数(非负整数)。棋盘第1格是唯一的起点,第N格是终点,游戏要求玩家控制一个乌龟棋子从起点出发走到终点.
乌龟棋中M张爬行卡片,分成4种不同的类型(M张卡片中不一定包含所有4种类型的卡片,见样例),每种类型的卡片上分别标有1、2、3、4四个数字之一,表示使用这种卡片后,乌龟棋子将向前爬行相应的格子数。游戏中,玩家每次需要从所有的爬行卡片中选择一张之前没有使用过的爬行卡片,控制乌龟棋子前进相应的格子数,每张卡片只能使用一次。游戏中,乌龟棋子自动获得起点格子的分数,并且在后续的爬行中每到达一个格子,就得到该格子相应的分数。玩家最终游戏得分就是乌龟棋子从起点到终点过程中到过的所有格子的分数总和。 很明显,用不同的爬行卡片使用顺序会使得最终游戏的得分不同,小明想要找到一种卡片使用顺序使得最终游戏得分最多。 现在,告诉你棋盘上每个格子的分数和所有的爬行卡片,你能告诉小明,他最多能得到 多少分吗?
输入的每行中两个数之间用一个空格隔开。 第1行2个正整数N和M,分别表示棋盘格子数和爬行卡片数。 第2行N个非负整数,a1a2……aN
其中ai表示棋盘第i个格子上的分数。 第3行M个整数,b1b2……bM
表示M张爬行卡片上的数字。 输入数据保证到达终点时刚好用光M张爬行卡片,即N - 1=∑(1->M) bi
输出一行一个整数
输入:
13 8
4 96 10 64 55 13 94 53 5 24 89 8 30
1 1 1 1 1 2 4 1
输出:
455
对于30%的数据有1 ≤ N≤ 30,1 ≤M≤ 12。
对于50%的数据有1 ≤ N≤ 120,1 ≤M≤ 50,且4 种爬行卡片,每种卡片的张数不会超过20。
对于100%的数据有1 ≤ N≤ 350,1 ≤M≤ 120,且4 种爬行卡片,每种卡片的张数不会超过40;0 ≤ ai ≤ 100,1 ≤ i ≤ N;1 ≤ bi ≤ 4,1 ≤ i ≤M。输入数据保证N−1=ΣMi
思路
穷举所有排列时间复杂度太大,考虑动态规划。 关键在于找出重叠子问题。
看完题目我先想到的是以棋子位置为状态,进行dp。 但是显然每种卡片使用的张数就无法控制了。
所以使用每种类型的爬行卡的张数描述状态。
显然起始状态为dp[0][0][0][0]
,最终状态为dp[m1][m2][m3][m4]
。
对于每个状态,下一张使用爬行卡片1的状态转移方程为
dp[i+1][j][k][l] = max(dp[i+1][j][k][l],a[i+j*2+k*3+l*4]+dp[i][j][k][l])
其他爬行卡片类似。
由于知道当前总张数,可以优化掉一个循环。 另外注意判断每种卡片的总张数。
例程
#include <cstdio>
#include <algorithm>
using namespace std;
const int M = 360;
const int N = 45;
int m,n;
int a[M];
int card[5];
int dp[N][N][N][N];
int main(){
scanf("%d%d",&m,&n);
for(int i=0;i<m;i++){
scanf("%d",&a[i]);
}
int t;
for(int i=0;i<n;i++){
scanf("%d",&t);
card[t]++;
}
dp[0][0][0][0] = a[0];
for(int i=0;i<n;i++){
for(int j=0;j<=i && j<=card[1];j++){
for(int k=0;k<=(i-j) && k<=card[2];k++){
for(int l=0;l<=(i-j-k) && l<=card[3];l++){
t = j + 2*k + 3*l + 4*(i-j-k-l);
if(i-j-k-l<=card[4]){
dp[j+1][k][l][i-j-k-l] = max(dp[j+1][k][l][i-j-k-l],\
dp[j][k][l][i-j-k-l] + a[t+1]);
dp[j][k+1][l][i-j-k-l] = max(dp[j][k+1][l][i-j-k-l],\
dp[j][k][l][i-j-k-l] + a[t+2]);
dp[j][k][l+1][i-j-k-l] = max(dp[j][k][l+1][i-j-k-l],\
dp[j][k][l][i-j-k-l] + a[t+3]);
dp[j][k][l][i-j-k-l+1] = max(dp[j][k][l][i-j-k-l+1],\
dp[j][k][l][i-j-k-l] + a[t+4]);
}
}
}
}
}
printf("%d\n",dp[card[1]][card[2]][card[3]][card[4]]);
return 0;
}