题解 P2964 【[USACO09NOV]硬币的游戏A Coin Game】

· · 题解

嗯既然还没有题解那么我就来一发吧……

DP: 1.状态:建立一个二维的状态(i,j)说明拿硬币的权力到达其中一名玩家时,桌面上还剩下编号为1~i(倒序,1为最底下的) 的硬币,

上一名玩家拿走了j枚硬币。

2.下一步的状态:那么这一个玩家在这一轮可以选择拿走1,2,3,4…2*j枚硬币,而他所能获得的最大硬币面值就是1~i所有的硬币面

值之和减去他完成此次操作后(设他取走了k枚硬币)到达状态(i-k,k)的另一名玩家所能获得的最大硬币数。

3.状态的转移:可是因为k的取值范围很大,所以不能直接枚举,不难发现(i,j-1)和(i,j)的状态只相差两种选择的可能,即下一步取走

2*j-1或2*j个硬币的这两种,只需要再比较一次这两种状态即可。

*状态转移方程:dp[i][j]=max(dp[i][j],sum[i]-dp[i-k][k],sum[i]-dp[i-k-1][k+1])(k==2\j-1);

4.答案:答案则是在剩下1~n枚硬币时(初始状态)的dp[n][1](下一步可以选择1枚或两枚硬币)了。

AC代码:

#include<bits/stdc++.h>
using namespace std;
int n,sum[3001],c[3001],dp[3001][3000]; //数组要开大一点,不然会WA;
int main()
{
    cin>>n;
    for(int i=n;i>=1;i--)cin>>c[i];//为了处理方便,我们直接逆序输入(编号自底向上)
    for(int i=1;i<=n;i++)sum[i]+=sum[i-1]+c[i];//获取前缀和
    for(int i=1;i<=n;i++)
        for(int j=1;j<=n;j++)
        {
            dp[i][j]=dp[i][j-1];//重合部分
            int k=2*j-1;
            if(k<=i)dp[i][j]=max(dp[i][j],sum[i]-dp[i-k][k]);//新增状态
            k+=1;
            if(k<=i)dp[i][j]=max(dp[i][j],sum[i]-dp[i-k][k]);//新增状态
        }
    cout<<dp[n][1]<<endl;
    return 0;
}