码迷,mamicode.com
首页 > 其他好文 > 详细

uva10891

时间:2017-10-31 11:01:54      阅读:142      评论:0      收藏:0      [点我收藏+]

标签:span   printf   一个   amp   size   using   转移   正数   ring   

两个人只能从一个序列的左边起或者右边起连续取数,他的得分为这些数的和,让你求先手最多可以比后手多多少分。

状态的设计:

可以发现无论两个人怎么取,留给下一个人决策的序列一定是连续的,我们可以根据这个性质设状态。

设d[i][j]表示从i到j这些数中,先手能比后手得到的最大分数。(这里我们不规定A、B,而是规定先手与后手,是因为先手后手这个状态更具普遍性,设AB更加麻烦一点)

又因为最后序列会被取完,所以要使我的得分最大就要使你的得分最小。

所以对于每一个状态,我们考虑最小化对手的得分,记为m,再用sum[i][j]减去m就是先手的最大得分。

注意m<=0,因为对于先手而言最差的情况就是后手不取。(如果后手取的得分是正数,则先手不划算,还不如后手不取,这样等价于先手取光)

如此写出状态转移方程:

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用前缀和,答案 = d[1][n] - (sum[1][n] - d[1][n])

#include <cstdio>
#include <algorithm>
#include <cstring>

using namespace std;

const int maxn = 105;

int n;

int s[maxn], vis[maxn][maxn], f[maxn][maxn];

int dp(int i, int j)
{
    if (vis[i][j]) return f[i][j];
    vis[i][j] = 1;
    int& ans = f[i][j];int mn = 0;//关键的一步
    for (int k = i + 1; k <= j; k++) mn = min(mn, dp(k, j));
    for (int k = i; k < j; k++) mn = min(mn, dp(i, k));
    return ans = s[j] - s[i - 1] - mn;
}

int main()
{
    while(scanf("%d", &n) && n)
    {
        memset(vis, 0, sizeof vis);
        for (int i = 1; i <= n; i++)
        {
            int x;
            scanf("%d", &x);
            s[i] = s[i - 1] + x;
        }
        printf("%d\n", 2 * dp(1, n) - s[n]);
    }
    return 0;
} 

 

uva10891

标签:span   printf   一个   amp   size   using   转移   正数   ring   

原文地址:http://www.cnblogs.com/yohanlong/p/7760201.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!