博弈论+dp——洛谷P2964 [USACO09NOV]硬币的游戏A Coin Game

https://daniu.luogu.org/problem/show?pid=2964
本来博弈论就不懂,现在套上dp,直接萎了;
题解都看了半天;
我们搞一个f[i][j]表示还剩1~i的时候,上一个人选了j个;
显然i=n的时候是最开始,就是一个都没取;
现在我们倒着dp,所以一开始我们的读入也要倒着读;
关于这个f[i][j],它代表一个选手的状态,所以到底是先手还是后手我们不知道;
但是答案显然就是f[n][1];
就是说现在还剩1~n的物品(就是一开始的状态),上一个人取1个,显然这一次我们可以取1或2个,这个和我们一开始题目要求的状态是一样的;
那么怎么求f[i][j]呢?
我们要枚举一个k,一个kk
f[i][j]=max(f[i][j],sum[i]-f[i-k][kk]);
然后我们就会发现一个事情;
对于f[i][j],k=1~(j-1)*2的答案已经在f[i][j-1]里面了;
所以我们直接算k=j*2和k=j*2+1就好啦;
然后对于kk,我们知道
f[i][j]在i一定的情况下,j越大越好
所以
f[i][j]=max(f[i][j],sum[i]-f[i-k][k]);

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define Ll long long
using namespace std;
int f[2005][2005],a[2005];
int n;
int main()
{
    scanf("%d",&n);
    for(int i=n;i;i--)scanf("%d",&a[i]);
    for(int i=1;i<=n;i++)a[i]=a[i-1]+a[i];
    for(int i=1;i<=n;i++)
    for(int j=1;j<=n;j++){
        f[i][j]=f[i][j-1];
        int k=j*2;
        if(i>=k)f[i][j]=max(f[i][j],a[i]-f[i-k][k]);
        k=j*2-1;
        if(i>=k)f[i][j]=max(f[i][j],a[i]-f[i-k][k]);
    }
    printf("%d",f[n][1]);
}
posted @ 2017-05-01 14:05  largecube233  阅读(216)  评论(0编辑  收藏  举报