Game of Sum UVA10891转自刘汝佳蓝书

题目大意;
给定一个连续的序列,A和B进行博弈,两个人轮流取数,A先取数,每次只能从序列的右端或者左端取一个数,不可以两端都取。所有数字被取完后游戏结束,统计每个人取走的所有数之和作为各自的得分.两人采取的策略都是让自己的得分尽量高,并且两人都十分聪明。

分析
博弈问题
这个题百思不得其解,尤其是关于状态转移的,不是规定取一个数为什么会会有那么多状态,先记下,回头在看看.

整数的总和是一定的,所以一个人的得分越高,相应的另一个人就越低,不管怎么取,对于任意时刻,游戏的状态是原始序列的一个子序列, 我们用d(i,j)表示序列的第i-j个元素组成的序列,在双方都采取最优策略的情况下,先手得分的最大值,;
状态转移时,我们需要枚举从左边取,还是从右边取以及需要取多少个,这等于说枚举给对方留下了什么样的子序列 是(k,j)(i<k<=j)还是(i,k)(i<=k<j) .因此状态转移方程如下;
d(i,j)=sum(i,j)-min{d(i+1,j),d(i+2,j)……d(j,j),d(i,j-1),d(i,j-2)……d(i,i),0}
sum(i,j)表示从i至j个序列的和;
0表示取完所有的数;
两人得分之和为sum(i,j),因此另外一人得分为sum(i,j)-d(i,j) 结果是d(i,j)-( sum(i,j)-d(i,j) )
转化为2*d(i,j)-sum(i,j)
核心代码

int dp(int i,int j) {

	if (vis[i][j]) return d[i][j];		//记忆化搜索
	v[i][j] = 1;
	int m = 0;
	for (int k = i + 1; k <= j; k++)   m = min(d(k, j), m);
	for (int k = j-1; k >=i; k--)   m = min(d(i, k), m);
	d[i][j] = sum[j] - sum[i - 1] - m;
	return d[i][j];
 }

回顾一下状态转移方程
d(i,j)=sum(i,j)-min{d(i+1,j),d(i+2,j)……d(j,j),d(i,j-1),d(i,j-2)……d(i,i),0}
我们令
f(i,j)=min{d(i,j),d(i+1,j),……d(j,j)}
g(i,j)=min{d(i,j),d(i,j-1)……d(i,i)}
于是乎,状态转移方程可以写成:
d(i,j) =sum(i,j)-min{f(i+1,j),g(i,j-1),0} f(i,j)和g(i,j)也可以快速计算出来
f(i,j)=min{d(i,j),f(i+1,j)}
g(i,j)=min{d(i,j),g(i,j-1)}

核心代码

void solve()
{
	for (int i = 1; i <= n; i++) f[i][i] = g[i][i] = d[i][i] = q[i];
	for (int len = 1; len < n; len++)
	{
		for (int i = 1; len + i <= n; i++)
		{
			int j = i + len;
			int m = 0;
			m = min(m, f[i+1][j]);
			m = min(m, g[i][j - 1]);
			d[i][j] = s[j]-s[i]-m;
			f[i][j] = min(f[i + 1][j], d[i][j]);
			g[i][j] = min(g[i][j - 1], d[i][j]);
		}
	}
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值