首页 > 代码库 > uva 10891 区间dp+记忆化搜索

uva 10891 区间dp+记忆化搜索

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 }

 

uva 10891 区间dp+记忆化搜索