【UVA - 10891 Game of Sum 】【HRBUST - 1622】 Alice and Bob (区间dp,博弈问题)

题干:

有一个长度为N的整数序列,Alice和Bob轮流取数,Alice先取。每次玩家只能从左端或者右端
取一个或多个数,但不能两端都取。所有数都被取走后游戏结束,然后统计每个人取走的所有数之和,
作为各自的得分。两个人采取的策略都是让自己的得分尽量高,并且两个人都足够聪明。

Input

输入第一行为组数T(T<=10)。

对于每组数据第一行为一个整数N(1<=N<=100),第二行为给定的整数序列。

整数的绝对值<=100

Output

对于每组数据,输出Alice和Bob都采取最优策略的情况下,Alice减去Bob的得分后的结果。

Sample Input

2
4
4 -10 -20 7
4
1 2 3 4

Sample Output

7
10

Hint

对于第一组数据: 第一步Alice从左端取1个数,第二步Bob从右端取一个数。 

第三步Alice从左端取一个数, 第四步只剩下一个数被Bob取走。

所以,Alice的得分减去Bob的得分为7。

对于第二组数据:Alice直接将全部数取走,所以Alice的得分减去Bob的得分为10。

题目大意:

   两个题是一样的题干,只是输入格式是不同的,Uva的是EOF结束,HRBUST的是输入样例个数t。

解题报告:

    可以知道整体石子的总和一定的,所以一个人的得分越高,另一个人的得分就越低。不管怎么取任意时刻游戏的状态都是原始序列的一段连续子序列(即被玩家取剩下的序列)。

  这里就可以用记忆化搜索了,用dp[i][j]表示在剩下i~j区间数字,在双方都采取最优策略的情况下,先手得分最大值。

     d[i][j] = sum[i][j] - min(dp(i + k, j), dp(i, j - k)); (1 <= k <= j - i + 1) ( sum[i][j] 表示i到j的前缀和之差)

AC代码:

#include
#define ll long long
using namespace std;
const int INF = 0x3f3f3f3f;
ll dp[205][205];
ll sum[205],a[205];
ll dfs(int l,int r) {
	if(dp[l][r] != -1) return dp[l][r];
	if(l == r) return dp[l][l]=a[l];
//	ll maxx = sum[r] - sum[l-1];
	ll maxx = -INF;
	for(int i = l+1; i<=r; i++) {
		maxx = max(maxx , sum[r] - sum[l-1] - dfs(i,r));
	}
	for(int i = r-1; i>=l; i--) {
		maxx = max(maxx , sum[r] - sum[l-1] - dfs(l,i));
	}
//	printf("dp[%d][%d] = %lld\n",l,r,maxx);
	maxx = max(maxx,sum[r] - sum[l-1]);
	return dp[l][r] = maxx;
}
int main()
{
	int t,n;
	cin>>t;
	while(t--) {
		scanf("%d",&n);
		memset(sum,0,sizeof sum);
		memset(dp,-1,sizeof dp);
		for(int i = 1; i<=n; i++) scanf("%lld",&a[i]),sum[i] = sum[i-1] + a[i];
		printf("%lld\n",2*dfs(1,n) - sum[n]);
	}
	return 0 ;
}

//18:04 - 18:17

总结:

   这样是可以AC的,或者直接用这一句

//    ll maxx = sum[r] - sum[l-1];

    总之,就是要把“一次全取光”这种情况也考虑进去。

你可能感兴趣的:(动态规划(dp),博弈问题,HRBUST,UVA)