uva10891 Game of Sum 博弈区间dp

// uva10891 Game of Sum
// 这是在训练指南上看到的一题,啃了很久很久,到现在有
// 一定的动态规划的基础,然而博弈性的东西依然不会
//
//
// 一开始想的是dp(i,j)表示在i,j段取得最大值
// dp(i,j) = max(dp(i,i),d(i.i+1),...d(i,j-1),d(j-1,j),d(j-2,j)...d(i+1)(j);,sum(i,j))
// 然而,连样例都没过。。。
//
// 把表打出来看了一下,发现只是每次取了当前的最大值,根本没有考虑到对手
// 能取得多少分。。。所以这样是错的肯定。。。最后看了书上的解法
//
//
// 书上的解法d(i,j)表示在i,j段的数中先手所能得到的最大值
// 则
// d(i,j) = sum(i,j) - min(d[i+1][j],d[i+2][j]..d[j][j] , d[i][i]
// d[i][i+1]...d[i][j-1])
// 理解是这样的。既然要求i,j段先手所能得到的最大值,则
// 应该是在i,j的某一个子段中获取的最小(下一个先手所获得的最大值)中选一个,
// 然后让这一段的总和,减去最小值,这样获得这i,j段先手获得的最大值
//
//
// 这算是博弈的区间dp,初次见识,非常的奇妙,注释的是自己写的错的代码
// 虽然是错的,但是留下来警醒自己!
//
// 哎。。。继续练吧。。。。

//const int maxn = 108;
//int a[maxn];
//int d[maxn][maxn];
//bool vis[maxn][maxn];
//int n;
//int sum[maxn];
//const int inf = 0x7f7f7f7f;
//void init(){
//	sum[0] = 0;
//	for (int i=1;i<=n;i++){
//		cin >> a[i];
//		sum[i] = sum[i-1] + a[i];
//	}
//	memset(vis,0,sizeof(vis));
//	for (int i=1;i<=n;i++)
//		for (int j=1;j<=n;j++)
//			d[i][j] = -inf;
//	for (int i=1;i<=n;i++)
//		d[i][i] = a[i];
//}
//
//void print(){
//	for (int i=1;i<=n;i++){
//		for (int j=1;j<=n;j++)
//			cout << d[i][j] << " ";
//		cout << endl;
//	}
//}
//
//
//int dp(int x,int y){
//	if (vis[x][y])	return d[x][y];
//	vis[x][y] = 1;
//	int &ans = d[x][y];
//	for (int i=x;i<y;i++)
//		ans = max(ans,max(dp(x,i),sum[y] - sum[x-1] - dp(x,i)));
//	for (int j=x+1;j<=y;j++)
//		ans = max(ans,max(dp(j,y),sum[y] - sum[x-1] - dp(j,y)));
//	ans = max(ans,sum[y]-sum[x-1]);
//	return ans;
//}
//
//void solve(){
//	cout << 2 * dp(1,n) - sum[n] << endl;
//}
//
//int main() {
//	freopen("G:\\Code\\1.txt","r",stdin);
//	while(cin>>n){
//		if (!n)
//			break;
//		init();
//		solve();
//		print();
//	}
//	return 0;
//}
#include <algorithm>
#include <bitset>
#include <cassert>
#include <cctype>
#include <cfloat>
#include <climits>
#include <cmath>
#include <complex>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <ctime>
#include <deque>
#include <functional>
#include <iostream>
#include <list>
#include <map>
#include <numeric>
#include <queue>
#include <set>
#include <stack>
#include <vector>
#define ceil(a,b) (((a)+(b)-1)/(b))
#define endl '\n'
#define gcd __gcd
#define highBit(x) (1ULL<<(63-__builtin_clzll(x)))
#define popCount __builtin_popcountll
typedef long long ll;
using namespace std;
const int MOD = 1000000007;
const long double PI = acos(-1.L);

template<class T> inline T lcm(const T& a, const T& b) { return a/gcd(a, b)*b; }
template<class T> inline T lowBit(const T& x) { return x&-x; }
template<class T> inline T maximize(T& a, const T& b) { return a=a<b?b:a; }
template<class T> inline T minimize(T& a, const T& b) { return a=a<b?a:b; }

const int maxn = 109;
int a[maxn];
int d[maxn][maxn];
int n;
bool vis[maxn][maxn];
int sum[maxn];
const int inf = 0x6f6f6f6f;
void init(){
	for (int i=1;i<=n;i++)
		cin>>a[i];
	sum[0] = 0;
	for (int i=1;i<=n;i++)
		sum[i] = sum[i-1] + a[i];
	memset(d,0,sizeof(d));
	memset(vis,0,sizeof(vis));
}

int dp(int x,int y){
	if (vis[x][y])	return d[x][y];
	vis[x][y] = 1;
	int ans = 0;
	for (int i=x;i<y;i++)	ans = min(ans,dp(x,i));
	for (int j=x+1;j<=y;j++)	ans = min(ans,dp(j,y));
	d[x][y] = sum[y] - sum[x-1] - ans;
	return d[x][y];
}

void solve(){
	printf("%d\n",2*dp(1,n)-sum[n]);
}

void print(){
	for (int i=1;i<=n;i++){
		for (int j=1;j<=n;j++)
			cout << d[i][j] << " ";
		cout << endl;
	}
}

int main() {
	//freopen("G:\\Code\\1.txt","r",stdin);
	while(cin>>n){
		if (!n)
			break;
		init();
		solve();
	//	print();
	}
	return 0;
}

时间: 2025-01-11 12:08:25

uva10891 Game of Sum 博弈区间dp的相关文章

UVA - 10891 Game of Sum (区间dp)

题意:AB两人分别拿一列n个数字,只能从左端或右端拿,不能同时从两端拿,可拿一个或多个,问在两人尽可能多拿的情况下,A最多比B多拿多少. 分析: 1.枚举先手拿的分界线,要么从左端拿,要么从右端拿,比较得最优解. 2.dp(i, j)---在区间(i, j)中A最多比B多拿多少. 3.tmp -= dfs(i + 1, r);//A拿了区间(l, i),B在剩下区间里尽可能拿最优 tmp是A拿的,dfs(i + 1, r)是B比A多拿的,假设dfs(i + 1, r)=y-x,y是B拿的,x是A

UVA 10891 Game of Sum(区间DP(记忆化搜索))

题目链接:https://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&page=show_problem&problem=1832 题目大意: 两个人在玩一个游戏: 给你一行n个数字,每次只能从左端或者右端取一个或多个数字. 每个人的分值就是他们各自取得的数字之和. 假设两人都足够聪明,问先手最多能比后手多多少分. 解题思路: 其实题目意思就是先手最多能得到多少分. 设dp[l][r]是取完[l,r]的

hdu3280Equal Sum Partitions (区间DP)

Problem Description An equal sum partition of a sequence of numbers is a grouping of the numbers (in the same order as the original sequence) in such a way that each group has the same sum. For example, the sequence: 2 5 1 3 3 7 may be grouped as: (2

UVA10891 Game of Sum 区间DP

好像带点博弈,又好像没有. 设dp[i][j] 为[i,j]区间内先手得分的最大值(这里的先手不一定是指player A!) 这时候只需要枚举出现在的先手会取哪一边,取几个,然后现在的最优状态就可以由以前的状态推出来 那么dp[i][j] = sum[i,j]-min(dp[i+1][j],dp[i+2][j],....,dp[j][j],dp[i][j-1],...,dp[i][i],0); 举个例子: 例如说sum[i,j]-dp[i+1][j]是指先手取了[i,j]的第一个元素即 A[i]

Uva 10891 Game of Sum(区间博弈dp)

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

UVA 10891 Game of Sum 区间dp

http://acm.hust.edu.cn/vjudge/problem/viewProblem.action?id=19461 题目意思大致是给你一串数字,A,B两个人轮流从两端取一段数字并得到该串数字的和的点数,每个人都尽可能的多的点数,问A最多能比B多多少点. 区间dp,一开始打算分AB,但是发现太麻烦了,最后用dp(l,r)表示在区间l~r中先手能赢的的最多点数.假设A是区间(l,r)的先手的话,如果A选择了(l,k )// 或(k+1,r)的数字,那他的得分(l,r)的总分减去B在余

UVA 10891 区间DP+博弈思想

很明显带有博弈的味道.让A-B最大,由于双方都采用最佳策略,在博弈中有一个要求时,让一方的值尽量大.而且由于是序列,所以很容易想到状态dp[i][j],表示序列从i到j.结合博弈中的思想,表示初始状态i->j情况下,先手能获得的最大分数.后手能获得的就是sum[i][j]-dp[i][j].接下来枚举先手选取的是两端的哪一段即可. #include <iostream> #include <cstdio> #include <cstring> using name

uva10891 - Game of Sum(递推,极大极小的思想)

题目:uva10891 - Game of Sum(递推) 题目大意:给出N个数,然后有两个小伙伴在玩游戏,每次可以从这一排数字的两侧中选择一侧开始取连续的数,必须取一个,也可以取完.这两个小伙伴都会采用最优的策略来取数,问第一个小伙伴取数的和与第2个小伙伴取数的和的差值. 解题思路:这题刚开始没什么头绪,只要碰到博弈思想的题目就没什么想法.看了别人的题解才明白. 先从简单的情况来讲如果每个小伙伴只能从两侧的一侧取一个数的话, dp[i][j]:小伙伴在第i个数字到第j个数字能取得的最大值: s

合并石子 区间dp水题

合并石子 链接: nyoj 737 描述: 有N堆石子排成一排,每堆石子有一定的数量.现要将N堆石子并成为一堆.合并的过程只能每次将相邻的两堆石子堆成一堆,每次合并花费的代价为这两堆石子的和,经过N-1次合并后成为一堆.求出总的代价最小值. tags:最基本的区间dp,这题范围小,如果n大一些,还是要加个平行四边行优化. #include<iostream> #include<cstdio> #include<cstdlib> #include<cstring&g