NOIP2017赛前模拟 Game DP (2017.10.30)

题目描述:
 给你N个数,每个数的大小为 ai ,小A和小B从前往后轮流选数,小A先手,第一次可以选1或2个数。后面,如果前一个人选了K个数,后一个人只能选K或K+1个数;
 小A和小B都是绝顶聪明,输出在双方都采取最优策略时,小A选的数的总和能比小B多多少。
数据范围: 1<=N<=20000;
样例:
输入
3
1 3 2
输出:
4

输入
10
1 2 3 4 5 6 7 8 9 10
输出
-13

题解:
 对于这种最优解的题,我们可以很自然的想到是DP,然后我们考虑转移:
 1、因为要满足无后效性,并且每次转移时都是由上一个状态的最优解转移过来,从前往后DP肯定是不行的,我们考虑从后往前DP;
 2、我们可以定义 DP[ i ] [ j ] ,表示当前取到第 i 位,取了 j 个数的最优解,我们发现这样的话对于后面的状态肯定是没有影响的;
 3、考虑转移:
  DP[ i ][ j ]=v[ i + j - 1 ]-v[ i -1 ] - max( DP [ i + j ][ j ] , DP[ i + j ][ j + 1 ] ),我们在后面取max,而不是min的原因是:两个人都是绝顶聪明,都希望自己最大。
  4、最后我们发现 j 最大是200,(等差数列)

#include
#include
#include
#include
#include
#include
#include
#include
#include
using namespace std;
const int INF = -0x3f3f3f3f;
const int N = 20005;
int T,n,v[N],lim[N],dp[N][205];

inline int Readint(){
    int i=0,f=1;char c;
    for(c=getchar();!isdigit(c);c=getchar());
    if(c=='-') f=-1,c=getchar();
    for(;isdigit(c);c=getchar()) i=(i<<1)+(i<<3)+c-'0';
    return i*f;
}

int main(){
    //freopen("game.in","r",stdin);

    T=Readint();
    for(int i=1,s=0;i<=20000;i++){
        if(s>=20000)break;
        for(int j=1;j<=20000-s;++j)lim[j]=i;
        s+=i;
    }
    while(T--){
        memset(dp,0,sizeof(dp));
        memset(v,0,sizeof(v));
        n=Readint();
        for(int i=1;i<=n;i++) v[i]=v[i-1]+Readint();
        for(int i=n;i;i--){
            for(int k=1;i+k-1<=n&&k<=i+1&&k<=200;k++)
                dp[i][k]=v[i+k-1]-v[i-1]-max(dp[i+k][k],dp[i+k][k+1]);
        }
        cout<1][1],dp[1][2])<<"\n";
    }
}

你可能感兴趣的:(考试总结)