题意: A和B两个人玩游戏,有一个数组a,两个人轮流取数,A先,A只能从左往右取,B只能从右往左取,并且如果前一个人取了k个数,那么接下来的那个人就得取k或者k+1个数,如果剩下的数不足k个,那么游戏结束,输出 “A取出的值的和” 减去 “B取出的数的和” 的差。A想让这个差尽可能的大,B想让这个差尽可能的小,两个人都采取最优解,问最后的差是多少。
在一篇博客上看到一句话:
这种两人博弈一般都可以用两个dp写, 一个dp描述第一个人的最优态, 第二个dp描述第二个人的最优态,难点在于优化空间。
思路:参考下面这篇博客,我也是看这个看懂的。
http://www.itdaan.com/blog/2017/05/31/e51a6afa17ed501cd1e1a7d84fc98101.html
AC code:
#include<bits/stdc++.h>
#define mem(a,b) memset((a),b,sizeof(a))
typedef long long ll;
const int N=100010;
using namespace std;
int n,a[4002];
int dp[4002][100][100][2];
bool vis[4002][100][100][2];
int sum[4002];
int solve(int l,int cnt,int pre,int cur)
{
/*
l表示A已经拿走了l个数
cnt表示B比A多拿了cnt个数
pre表示上一个人上次拿了pre个数
如果cur==0表示这次该A拿了,cur==1表示这次该B拿了
*/
if(vis[l][cnt][pre][cur])
return dp[l][cnt][pre][cur];
vis[l][cnt][pre][cur]=true;
int r=l+cnt;
if(n-l-r<pre)
return dp[l][cnt][pre][cur]=sum[l]-(sum[n]-sum[n-r]);
int ret=0;
if(cur==0)
{
ret=solve(l+pre,cnt-pre,pre,1);
if(n-l-r>pre)
ret=max(ret,solve(l+pre+1,cnt-pre-1,pre+1,1));
}
else
{
ret=solve(l,cnt+pre,pre,0);
if(n-l-r>pre)
ret=min(ret,solve(l,cnt+pre+1,pre+1,0));
}
return dp[l][cnt][pre][cur]=ret;
}
int main()
{
scanf("%d",&n);
sum[0]=0;
for(int i=1;i<=n;i++)
{scanf("%d",&a[i]);sum[i]=sum[i-1]+a[i];}
mem(vis,false);
int ans=solve(0,0,1,0);
printf("%d",ans);
return 0;
}