首页 > 代码库 > 区间DP lightoj 1031

区间DP lightoj 1031

在此游戏中任意时刻的状态都是原始序列的一段子序列故:

定义d(i, j) : 表示原来序列的第i ~ j个元素组成的子序列,在双方都采取最优策略的情况下,先手得分的最大值、

状态转移时,需要枚举从左边或者从右边取多少个。因此

d(i, j) = sum[i, j] - min{d(i+1, j), d(i + 2, j).....d(j, j) , d(i, j-1), d(i, j-2),, ... , d(i, i), 0}

其中sum[i, j] 是元素i 到j 的和。这里的0是取完所有的数, 有了它方程就不需要显式的边界条件了。

答案是 d(1, n) - (sum[1, n] - d(1, n)) = 2*d(1, n) - sum[1, n]

           先手                       后手

记忆话一下就可以了

 1 #include<stdio.h>
 2 #include<string.h>
 3 #include<algorithm>
 4 
 5 using namespace std;
 6 #define MAXN 110
 7 #define inf  100000000
 8 
 9 int dp[MAXN][MAXN];
10 int z[MAXN];
11 int sum[MAXN];
12 bool vis[MAXN][MAXN];
13 
14 
15 int d(int a,int b)
16 {
17     if(vis[a][b])
18         return dp[a][b];
19     vis[a][b]=1;
20     int m=0;
21     int i;
22     for(i=a+1;i<=b;i++)m=min(m,d(i,b));
23     for(i=a;i<=b;i++)m=min(m,d(a,i));
24     dp[a][b]=sum[b]-sum[a-1]-m;
25     return dp[a][b];
26 }
27 int main()
28 {
29     int t,ca;
30     scanf("%d",&t);
31     ca=1;
32 
33     while(t--)
34     {
35         int n,i;
36         scanf("%d",&n);
37         memset(vis,0,sizeof(vis));
38         memset(dp,0,sizeof(dp));
39         for(i=1;i<=n;i++)
40         {
41             scanf("%d",&z[i]);
42             sum[i]=sum[i-1]+z[i];
43         }
44 
45         printf("Case %d: %d\n",ca++,2*d(1,n)-sum[n]);
46     }
47     return 0;
48 }

 

区间DP lightoj 1031