题意:
给定n个数字,A和B可以从这串数字的两端任意选数字,一次只能从一端选取。
并且A B都尽力使自己选择的结果为最大的,可以理解成A B每一步走的都是最优的。
如果A先选择,则A B差值最大是多少。
思路:
对于此数列,假如面对的是(i,j)这个区间。(i,j 代表第i、j个数)
dp(i,j)代表先手所得到的最大值。
但对于这个决策,我们需要他的分成两份的子区间的信息。(比如【1,4】可以分成【1,1】【2,4】或者【1,2】【3,4】或者【1,3】【4,4】)
因为要取得的是最大的,所以我们只要知道他分成的子区间取得的最小值(相当于后手选最小值),再用sum【i,j】减去这个值就是正解了。
答案为A-B的得分,所以ans=dp(1,n)- (sum(1,n)-dp(1,n))= 2 * dp(1,n) - sum(1,n)。
我这份代码里的关键是
mem[i][j] = max(sum[j]-sum[i-1],mem[i][j]); //全取的情况。
for(int k = i;k < j;k++){//分成两份的情况。
mem[i][j] = max(mem[i][j],sum[j]-sum[i-1]-min(dp(i,k),dp(k+1,j)));
}
AC代码:
#include <iostream>
#include <cstdio>
#include <string.h>
#define INF 0x3f3f3f3f
using namespace std;
int a[110];
int mem[110][110];
bool vis[110][110];
int sum[110];
int dp(int i,int j){
if(vis[i][j]) return mem[i][j];
mem[i][j] = max(sum[j]-sum[i-1],mem[i][j]);
for(int k = i;k < j;k++){
mem[i][j] = max(mem[i][j],sum[j]-sum[i-1]-min(dp(i,k),dp(k+1,j)));
}
vis[i][j] = true;
return mem[i][j];
}
int main()
{
int n;
while(scanf("%d",&n),n){
memset(mem,-0x3f3f3f3f,sizeof(mem));
memset(vis,false,sizeof(vis));
for(int i = 1;i <= n;i++){
scanf("%d",a+i);
mem[i][i] = a[i];
vis[i][i] = true;
}
sum[0] = 0;
for(int i = 1;i <= n;i++){
sum[i] = sum[i-1] + a[i];
}
printf("%d\n",2*dp(1,n)-sum[n]);
}
return 0;
}