Uva 10891 Game of Sum
题目地址:Uva 10891
题目大意,有一个包含n(n<=100)个整数的数列,两个人轮流从中取数,每次只能从数列的某一端(左或是右)取,每次可以去出一个或多个,但不能不取,每次所取得的数的和为每人的总得分,问在两人均采取最优策略时先手能够比后手多得多少分。
题目分析:看到“最优”,以及数据范围应该能想到是n^3 DP,而本题的难点在于状态的表示。而我们可以发现对于一整个的数列两人的得分总和是一定的,因此只需要算先手的人的总得分就可以算出两者的差。用dp[i][j]表示在i~j区间内先手能得到的最大得分,而剩下的就是后手得到的,但是由于两人都是采用最优的方式,因此后手得的分也应该在dp的某个状态之中,所以转移方程为
for(int k=i;k<=i+j;k++)
u=max(u,sum[i][i+j]-dp[i][k]);
for(int k=i+j;k>=i;k--)
u=max(u,sum[i][i+j]-dp[k][i+j]);
dp[i][i+j]=u;
最后再算出答案
ans=dp[1][n]-(sum[n]-dp[1][n])=2*dp[1][n]-sum[n]
最后附上完整代码
#include<stdio.h>
#include<algorithm>
#include<string.h>
#define max(a,b)(a>b?a:b)
#define min(a,b)(a<b?a:b)
int dp[110][110],n,m,arr[110],sum[110][110],s[110];
int main()
{
while(scanf("%d",&n)){
if(n==0)break;
memset(dp,0,sizeof(dp));
memset(sum,0,sizeof(sum));
for(int i=1;i<=n;i++){
scanf("%d",&arr[i]);
s[i]=s[i-1]+arr[i];
}
for(int i=1;i<=n;i++)
for(int j=i;j<=n;j++)
sum[i][j]=s[j]-s[i-1];
for(int j=0;j<=n;j++){
for(int i=1;i<=n;i++){
if(i+j>n)break;
int u=-0x7fffffff;
for(int k=i;k<=i+j;k++)
u=max(u,sum[i][i+j]-dp[i][k]);//保证此次取出的子序列尽可能大,且对手所取得依然是最优的情况
for(int k=i+j;k>=i;k--)
u=max(u,sum[i][i+j]-dp[k][i+j]);
dp[i][i+j]=u;
}
}
int ans=dp[1][n]*2-sum[1][n];
printf("%d\n",ans);
}
return 0;
}