【NOIP2010】乌龟棋
本题在洛谷上的链接:https://www.luogu.org/problemnew/show/P1541
呃呃,确实想不粗来。虽然最近做了好几道DP题,但还是无法保证想得出状态以及状态转移方程。。。不过顺便了解到了DP和贪心的“血缘关系”:如果对于第i个状态,只需第i-1个状态就可以推出第i个状态,对应的推导过程就叫贪心,这种推导过程也是数学上常用的数学归纳法;如果对于第i个状态,仅仅由第i-1个状态无法推出,而需要综合考虑前i-1个状态才能推出,对应的推导过程就叫动态规划,这种方法也叫第二数学归纳法。
好了,言归正传,我们可以定义状态dp[i][j][k][l]表示这四种卡片的使用数量(状态的得出可以参考数据范围的提示:只有四种卡牌,每种数量不超过40),那么dp[i][j][k][l]=max(dp[i-1][j][k][l],dp[i][j-1][k][l],dp[i][j][k-1][l],dp[i][j][k][l-1])+a[1+i+2*j+3*k+4*l],特别的,dp[0][0][0][0]=a[1],一张都不使用时,棋子处于起点,也有相应分数。具体细节详见代码。
1 #include<cstdio> 2 inline int max(int a,int b) {return a>b?a:b;} 3 const int maxn=355,maxm=125,maxe=45; 4 int n,m,a[maxn],b,num[5],dp[maxe][maxe][maxe][maxe],ans; 5 int main() { 6 scanf("%d%d",&n,&m); 7 for(int i=1;i<=n;++i) scanf("%d",&a[i]); 8 for(int i=1;i<=m;++i) {scanf("%d",&b);++num[b];} 9 dp[0][0][0][0]=a[1]; 10 for(int i=0;i<=num[1];++i) 11 for(int j=0;j<=num[2];++j) 12 for(int k=0;k<=num[3];++k) 13 for(int l=0;l<=num[4];++l) { 14 int to=i+2*j+3*k+4*l+1; 15 if(i>0) dp[i][j][k][l]=max(dp[i][j][k][l],dp[i-1][j][k][l]+a[to]); 16 if(j>0) dp[i][j][k][l]=max(dp[i][j][k][l],dp[i][j-1][k][l]+a[to]); 17 if(k>0) dp[i][j][k][l]=max(dp[i][j][k][l],dp[i][j][k-1][l]+a[to]); 18 if(l>0) dp[i][j][k][l]=max(dp[i][j][k][l],dp[i][j][k][l-1]+a[to]); 19 } 20 printf("%d",dp[num[1]][num[2]][num[3]][num[4]]); 21 return 0; 22 }