原题:https://vjudge.net/problem/UVA-10891
题意:一个长度为n的序列,两个人A,B轮流取,A先取,每次玩家只能从左端或者右端取数,所有数字取走后游戏结束,统计所取得数字之和,求A的得分减B的得分的最大值。 A,B都足够聪明因此他们都能做出最优的决策。
数据范围: 1<=n<=100.
对于这类型的题目不需要分开考虑A,B,因为A,B都做出自己最优的决策,只需要将A,B统一为先手取就行了,这会方便很多。
dp[i][j]:序列下标为i~j的序列先手方取所能得到的最优结果。( 最优结果 指 先手 - 后手 的最大值)
状态转移方程:
怎么从子状态转移到dp[i][j]呢,根据题意每次从左边或者右边取,所以应该将所有可能的情况都遍历一次
for(int k=i;k<=j;k++){
dp[i][j]=max(sum[i][k]-dp[k+1][j],dp[i][j]);
dp[i][j]=max(sum[k][j]-dp[i][k-1],dp[i][j]);
}
(sum【i】【j】 指的是 i~j的和)
#include<bits/stdc++.h>
using namespace std;
const int maxn=102;
const int minn=-0x3f3f3f3f;
int dp[maxn][maxn],num[maxn],sum[maxn][maxn];
int main(){
int n;
memset(sum,0,sizeof(sum));
while(scanf("%d",&n),n){
memset(dp,0,sizeof(dp));
for(int i=1;i<n;i++){ //漏了这个后面dp遇到num是负数会wa
for(int j=i+1;j<=n;j++) dp[i][j]=minn;
}
for(int i=1;i<=n;i++){
scanf("%d",num+i);
sum[1][i]=sum[1][i-1]+num[i];
}
for(int i=2;i<=n;i++){
for(int j=i;j<=n;j++){
sum[i][j]=sum[i][j-1]+num[j];
}
}
for(int i=1;i<=n;i++) dp[i][i]=num[i];
for(int dis=1;dis<n;dis++){ // dis=distant
for(int i=1;i<=n-dis;i++){
int j=i+dis;
for(int k=i;k<=j;k++){
dp[i][j]=max(sum[i][k]-dp[k+1][j],dp[i][j]);
dp[i][j]=max(sum[k][j]-dp[i][k-1],dp[i][j]);
}
}
}
cout<<dp[1][n]<<endl;
}
return 0;
}