题目链接:https://vjudge.net/problem/UVA-10891
题意:n个数的序列,A和B两个人比赛,A先取,可以从剩下序列的左边或者右边开始取,每次任取1~n个数字,两人都用最优方案,求A的总和减去B的总和
思路:dp[i][j]表示区间i~j先手能获得的最大值。
dp[i][j]=sum[i][j]-min{dp[i+1][j]......dp[j][j],dp[i][j-1].....dp[i][i],0};
#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;
int n;
int A[110],dp[110][110],sum[110];
int total(int l,int r)
{
return sum[r]-sum[l-1];
}
int main()
{
// freopen("rand.txt","r",stdin);
// freopen("out1.txt","w",stdout);
while(~scanf("%d",&n),n)
{
for(int i=1; i<=n; i++)
{
scanf("%d",&A[i]);
sum[i]=sum[i-1]+A[i];
}
for(int i=1; i<=n; i++) dp[i][i]=A[i];
for(int len=1; len<=n; len++)
{
for(int i=1; i<=n; i++)
{
int j=i+len;
if(j>n) break;
dp[i][j]=total(i,j);//先手全选的情况
for(int k=i+1; k<=j; k++)
dp[i][j]=max(dp[i][j],total(i,j)-dp[k][j]);
for(int k=j-1; k>=i; k--)
dp[i][j]=max(dp[i][j],total(i,j)-dp[i][k]);
}
}
printf("%d\n",2*dp[1][n]-total(1,n));
}
}
白书优化后:
#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;
int n;
int A[110],dp[110][110],sum[110],f[110][110],g[110][110];
int total(int l,int r)
{
return sum[r]-sum[l-1];
}
int main()
{
// freopen("rand.txt","r",stdin);
// freopen("out1.txt","w",stdout);
while(~scanf("%d",&n),n)
{
for(int i=1; i<=n; i++)
{
scanf("%d",&A[i]);
sum[i]=sum[i-1]+A[i];
}
for(int i=1; i<=n; i++) f[i][i]=g[i][i]=dp[i][i]=A[i];
for(int len=1; len<=n; len++)
{
for(int i=1; i<=n; i++)
{
int j=i+len;
if(j>n) break;
int m=0;
m=min(m,f[i+1][j]);
m=min(m,g[i][j-1]);
dp[i][j]=total(i,j)-m;
f[i][j]=min(dp[i][j],f[i+1][j]);
g[i][j]=min(dp[i][j],g[i][j-1]);
}
}
printf("%d\n",2*dp[1][n]-total(1,n));
}
}
见白书69页