【动态规划】P1541 乌龟棋
https://www.luogu.com.cn/problem/P1541
P1541 乌龟棋【NOIp提高组2010】【普及+/提高】 题解
这是一道dp题目。关于dp题,在设计状态的时候,要想清楚在整个状态空间中发生变化的量有哪些。
对于这个题目,我们可以找到以下的“变量”:
1.棋子所在的位置;
2.所用的各个种类的牌的数量。(实际上算4个变量)
于是我们很容易想到使用五维数组 f [ i ][ a ][ b ][ c ][ d ] 来表示:走到第 i 个格子,用了标有数字 1~4 的卡牌分别有 a,b,c,d 张,所得到的最大分数。
我们还可以继续优化。很明显,如果a,b,c,d都知道的话,i 是可以推出来的,即1+a+2b+3c+4d(由于本来棋子就在第1格,所以要加1)。这时 i 叫做“冗余状态",可以省去,用 f [ a ][ b ][ c ][ d ] 表示用了标有数字 1~4 的卡牌分别有 a,b,c,d 张,能得到的最大分数。
状态转移:由于f [ a ][ b ][ c ][ d ]可以由f [ a-1 ][ b ][ c ][ d ],f [ a ][ b-1 ][ c ][ d ],f [ a ][ b ][ c-1 ][ d ]和f [ a ][ b ][ c ][ d-1 ]转移而来,再加上 k [ 1+a+2b+3c+4d ]即可。其中 k [ i ] 表示第 i 个格子的分数。上述所有条件取一个最大值即可。注意 a,b,c,d 均不能超过题目中所给的每种卡牌对应的数量。
初始状态:f [ 0 ][ 0 ][ 0 ][ 0 ] = k[ 1 ]。因为乌龟棋本来就在第一个格子上。
目标:f [ am ] [ bm ][ cm ][ dm ] 。其中 am 表示 标有数字1的卡牌的数量,其他的以此类推。
#include<cstdio> #include<iostream> #include<cstring> #include<algorithm> #define MAXN 1001 #define ll long long #define re register using namespace std; int card[5]; inline int get_i(int a,int b,int c,int d) { return 1+a+b*2+c*3+d*4; } int n,m; int k[MAXN]; int f[41][41][41][41]; int main() { ios::sync_with_stdio(false); cin>>n>>m; for(int i=1;i<=n;i++) { cin>>k[i]; } int tmp; for(int i=1;i<=m;i++) { cin>>tmp; card[tmp]++; } //dp f[0][0][0][0]=k[1]; 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++) { int now=get_i(a,b,c,d); if(a!=0) f[a][b][c][d]=max(f[a][b][c][d],f[a-1][b][c][d]+k[now]); if(b!=0) f[a][b][c][d]=max(f[a][b][c][d],f[a][b-1][c][d]+k[now]); if(c!=0) f[a][b][c][d]=max(f[a][b][c][d],f[a][b][c-1][d]+k[now]); if(d!=0) f[a][b][c][d]=max(f[a][b][c][d],f[a][b][c][d-1]+k[now]); } } } } cout<<f[card[1]][card[2]][card[3]][card[4]]<<endl; return 0; }