NOIP2017赛前模拟 Game DP (2017.10.30)

来源:互联网 发布:c语言的延时函数 编辑:程序博客网 时间:2024/06/07 17:45

题目描述:
 给你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<cstdio>#include<algorithm>#include<cmath>#include<ctime>#include<cstring>#include<string>#include<iomanip>#include<iostream>#include<cctype>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<<max(dp[1][1],dp[1][2])<<"\n";    }}
原创粉丝点击