现在的位置: 首页 > 综合 > 正文

UVA 10891 Game of Sum (动态规划)

2018年02月23日 ⁄ 综合 ⁄ 共 1889字 ⁄ 字号 评论关闭

题意:有一个长度为n的整数序列,A和B轮流取数,A先取,每次可以从左端或者右端取一个或多个数,所有数都被取完时游戏结束,然后统计每个人取走的所有数字之和作为得分,两人的策略都是使自己的得分尽可能高,并且都足够聪明,求A的得分减去B的得分的结果。

思路:很容易想到一个复杂度是O(n^3)的方法,设dp(i,j)为第i个到第j个数时,先手能获得的最大分数,注意到对于一段整数序列,先手的得分与后手的得分之和就是这段序列和,所以后手的得分就是sum(i,j)-dp(i,j)。那么我们只要枚举先手的每种选择就可以了:dp(i,j)=sum(i,j)-min{ dp(i+1,j),dp(i+2,j).....dp(j,j),   dp(i,j-1),dp(i,-2j)....dp(i,i)
,0}。这个算法复杂度是O(n^3)

其实我们可以优化到O(n^2)

但是深入分析后可以知道,我们只要记录f(i,j)=min{dp(i,j),dp(i+1,j).....dp(j,j)},g(i,j)=min{dp(i,j),dp(i,-1j)....dp(i,i)}就可以了,那么dp(i,j)=min{f(i,j),g(i,j),0}

而f(i,j)=min{f(i+1),dp(i,j)}   g(i,j)=min{g(i,j-1),dp(i,j)}。那么这个题目就可以O(n^2)解决了。

总结:以后dp的基础上,观察每次递推所用到的值,如果用到的值本身还可以递推,那么我们就可以优化了

首先来一个O(n^3)的代码

#include<cstdio>
#include<cstring>
#include<iostream>
#define MAXN 105
using namespace std;
int n,sum[MAXN],f[MAXN][MAXN];
int main()
{
	sum[0]=0;
	while(~scanf("%d",&n) && n)
	{
		for(int i=1;i<=n;++i) {scanf("%d",&f[i][i]);sum[i]=f[i][i]+sum[i-1];}
		for(int k=1;k<n;++k)
		for(int i=1;i+k<=n;++i)
		{
			f[i][i+k]=sum[i+k]-sum[i-1];
			for(int p=i;p<i+k;++p) f[i][i+k]=max(sum[i+k]-sum[i-1]-f[p+1][i+k],f[i][i+k]);
			for(int p=i+k;p>i;--p) f[i][i+k]=max(sum[i+k]-sum[i-1]-f[i][p-1],f[i][i+k]);
		}
		printf("%d\n",2*f[1][n]-sum[n]+sum[0]);
	}
	return 0;
}

下面是O(n^2)的代码

#include<cstdio>
#include<cstring>
#include<iostream>
#define MAXN 105
using namespace std;

int f[MAXN][MAXN],g[MAXN][MAXN],dp[MAXN][MAXN],sum[MAXN],n;

int main()
{
	freopen("in.txt","r",stdin);
	sum[0]=0;//初始化sum[0]=0
	while(~scanf("%d",&n) && n)
	{
		for(int i=1;i<=n;++i) 
		{
			scanf("%d",&dp[i][i]);//本来是时读入到a[i]的,但是注意到dp[i][i]=a[i],那么直接读入到dp[i][i]省空间
			f[i][i]=g[i][i]=dp[i][i];//同理f[i][i]=g[i][i]=dp[i][i]
			sum[i]=sum[i-1]+dp[i][i];//sum[i]记录前i个数的和
		}
		
		for(int k=1;k<n;++k)
		for(int i=1;i+k<=n;++i)
		{
			int j=i+k;
			dp[i][j]=sum[j]-sum[i-1]-min(0,min(f[i+1][j],g[i][j-1]));//dp(i,j)=sum(i,j)-min(g(i,j-1),0,f(i+1,j))
			f[i][j]=min(f[i+1][j],dp[i][j]); g[i][j]=min(g[i][j-1],dp[i][j]);
			//f(i,j)=min(f(i+1,j),dp(i,j))     //g(i,j)=min(g(i,j-1),dp(i,j))
		}
		
		printf("%d\n",2*dp[1][n]-sum[n]);
	}
	return 0;
}

抱歉!评论已关闭.