https://vjudge.net/problem/UVA-10891
给定一个序列x,A和B依次取数,规则是每次只能从头或者尾部取走若干个数,A和B采取的策略使得自己取出的数尽量和最大,A是先手,求最后A-B的得分。
令 f(i,j)表示对于[i,j]对应的序列,先手可以从中获得的最大得分,那么答案可以写为 f(i,j)-(sum(i,j)-f(i,j)),也就是 2*f(i,j)-sum(i,j)
下面讨论f(i,j)的写法,显然递归的形式更好表达一些,为了防止重复的计算使用记忆化搜索。
当(i==j)时显然返回a[i]即可;
否则,我们可以枚举出所有先手可能拿走的数的区间,得出一个最大值就是返回值。
方程就是 f(i,j)=MAX{sum(i,k)+sum(K+1,j)-f(k+1,j) , sum(k,j)+sum(i,k-1)-f(i,k-1) | i<=k<=j}; //[i,k]和[k,j]是先手可能取得区间
还有一个转移方程是 f(i,j)=sum(i,j)-MIN(f(k1,k2)) | [k1,k2]是先手可能取得区间对应的补集。
1 #include<bits/stdc++.h> 2 using namespace std; 3 int pre[105],a[105]; 4 int ans[105][105],vis[105][105]; 5 int sum(int i,int j) 6 { 7 if(i>j) return 0; 8 return pre[j]-pre[i-1]; 9 } 10 int f(int l,int r) 11 { 12 if(l>r) return 0; 13 if(vis[l][r]) return ans[l][r]; 14 vis[l][r]=1; 15 if(l==r) return ans[l][r]=a[l]; 16 int ret=-999999999; 17 for(int k=l;k<=r;++k) 18 { 19 ret=max(ret,max(sum(l,k)+sum(k+1,r)-f(k+1,r),sum(k,r)+sum(l,k-1)-f(l,k-1))); 20 } 21 return ans[l][r]=ret; 22 } 23 int main() 24 { 25 int N,m,i,j,k; 26 while(cin>>N&&N){ 27 memset(vis,0,sizeof(vis)); 28 for(i=1;i<=N;++i) 29 { 30 cin>>a[i]; 31 pre[i]=pre[i-1]+a[i]; 32 } 33 cout<<2*f(1,N)-pre[N]<<endl; 34 } 35 return 0; 36 }