还有一道题和这道差不多:codeforces 737 D 题解链接:https://blog.csdn.net/flyzer/article/details/90204019
题意:A和B两个人玩游戏,有一个数组a,两个人轮流取数,A先,两个人都是只能从左往右取,并且如果前一个人取了k个数,那么接下来的那个人就得取k或者k+1个数,如果剩下的数不足k个,那么游戏结束,输出 “A取出的值的和” 减去 “B取出的数的和” 的差。A想让这个差尽可能的大,B想让这个差尽可能的小,两个人都采取最优解,问最后的差是多少。
在一篇博客上看到一句话:
这种两人博弈一般都可以用两个dp写, 一个dp描述第一个人的最优态, 第二个dp描述第二个人的最优态,难点在于优化空间。
hdu 6199 和 cf 737 D 题目的区别在于,hdu这道题两个人都是从左往右取,而cf这道题是第一个人从左往右取,第二个人从右往左取,还有就是数据范围的差别,其他的完全一样。
cf那道题只要记忆化搜索就行了,但是hdu这道题不行,因为会MLE,所以只能动态规划,由于只跟后200个左右的状态有关,我们考虑用滚动数组的方式来储存dp。
hdu这道题推荐这篇博客:https://blog.csdn.net/V5ZSQ/article/details/79325575
AC code:
(因为是多组输入,不要忘了对dp数组进行初始化,否则会WA)
//#pragma comment(linker, "/STACK:102400000,102400000")
#include<bits/stdc++.h>
#define mem(a,b) memset((a),b,sizeof(a))
typedef long long ll;
#define Mod 233 //只要大于等于200就行
using namespace std;
int n,a[20001];
int dp[2001][255][2];
int sum[20001];
int main()
{
int T; scanf("%d",&T);
while(T--)
{
scanf("%d",&n);
sum[0]=0;
for(int i=1;i<=n;i++)
{scanf("%d",&a[i]);sum[i]=sum[i-1]+a[i];}
int ans=0;
mem(dp,0);
//从第i个石子开始取且上一轮对手取了j个
for(int i=n;i>=1;i--)
for(int j=200;j>=1;j--)
{
if(n-i+1<j)
continue;
dp[i%Mod][j][0]=dp[(i+j)%Mod][j][1]+(sum[i+j-1]-sum[i-1]);
dp[i%Mod][j][1]=dp[(i+j)%Mod][j][0]-(sum[i+j-1]-sum[i-1]);
if(n-i+1>j)
{
dp[i%Mod][j][0]=max(dp[i%Mod][j][0],dp[(i+j+1)%Mod][j+1][1]+(sum[i+j]-sum[i-1]));
dp[i%Mod][j][1]=min(dp[i%Mod][j][1],dp[(i+j+1)%Mod][j+1][0]-(sum[i+j]-sum[i-1]));
}
}
printf("%d\n",dp[1][1][0]);
}
return 0;
}