Root | |
10891 - Game of SumTime limit: 3.000 seconds |
Input
The input consists of a number of cases. Each case starts with a line specifying the integer n (0 <
n ≤ 100), the number of elements in the array. After that, n numbers are given for the game. Input is
terminated by a line where n = 0.
Output
For each test case, print a number, which represents the maximum difference that the first player
obtained after playing this game optimally.
Sample Input
4
4 -10 -20 7
4
1 2 3 4
0
Sample Output
7
10
题意是可以理解为有n堆石头,A跟B两个人一次从中拿石头,每人最多可连续拿k堆,也就是不限制,A先拿。问两者都在最优的情况下,A能比B多拿几个。
首先这属于博弈问题,菜鸡对博弈了解很少,区间DP还是可以。就是枚举区间i,j,然后不断取值的操作。需要先对原数组进行处理,两层for循环,外层的i从0到n-1,内层的j从i到最后,sum数组储存从i到j的和。然后进行dfs。剩下的就是区间dp+记忆化搜索了,具体实现还是看代码吧,一目了然。
代码实现:
#include<iostream>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<queue>
#include<cstdio>
#define ll long long
#define mset(a,x) memset(a,x,sizeof(a))
using namespace std;
const double PI=acos(-1);
const int inf=0x3f3f3f3f;
const double esp=1e-6;
const int maxn=250005;
const int mod=1e9+7;
int dir[4][2]={0,1,1,0,0,-1,-1,0};
ll gcd(ll a,ll b){return b?gcd(b,a%b):a;}
ll lcm(ll a,ll b){return a/gcd(a,b)*b;}
ll inv(ll b){if(b==1)return 1; return (mod-mod/b)*inv(mod%b)%mod;}
ll fpow(ll n,ll k){ll r=1;for(;k;k>>=1){if(k&1)r=r*n%mod;n=n*n%mod;}return r;}
int dp[105][105],map[105],visit[105][105],sum[105][105],n;
int dfs(int l,int r)
{
if(l>r)
return 0;
if(visit[l][r]) //如果访问,返回DP[l][r]
return dp[l][r];
int ans=-1e9;
visit[l][r]=1; //标记
for(int k=1;k<=r-l+1;k++)
{
ans=max(ans,sum[l][r]-min(dfs(l+k,r),dfs(l,r-k))); //ans储存每次取值能得到的最大值,用sum[l][r]减去区间内最小的值就是最大值
}
dp[l][r]=ans; //记忆化
return ans;
}
int main()
{
int i,j,k;
while(cin>>n&&n)
{
mset(dp,0);
mset(visit,0);
for(i=0;i<n;i++)
cin>>map[i];
for(i=0;i<n;i++)
{
int summ=0;
for(j=i;j<n;j++)
{
summ+=map[j];
sum[i][j]=summ;
}
}
int ans=2*dfs(0,n-1)-sum[0][n-1]; //sum数组包含dfs得到的区间和其余区间,此处要求sum中除去dfs得到的值,并计算差值
cout<<ans<<endl;
}
return 0;
}