【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 }
AC代码
原文地址:https://www.cnblogs.com/Mr94Kevin/p/9630181.html