UVA 10891 - Game of Sum (博弈+区间DP+记忆化搜索)

Root Submit Problem Stats
uDebug Download as PDF

10891 - Game of Sum

Time limit: 3.000 seconds
This is a two player game. Initially there are n integer numbers in an array and players A and B get chance to take them alternatively. Each player can take one or more numbers from the left or right end of the array but cannot take from both ends at a time. He can take as many consecutive numbers as he wants during his time. The game ends when all numbers are taken from the array by the players. The point of each player is calculated by the summation of the numbers, which he has taken. Each player tries to achieve more points from other. If both players play optimally and player A starts the game then how much more point can player A get than player B?


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;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值