UVa 10891 Game of Sum(经典博弈区间DP)

题意:

给定n个数字,A和B可以从这串数字的两端任意选数字,一次只能从一端选取。

并且A B都尽力使自己选择的结果为最大的,可以理解成A B每一步走的都是最优的。

如果A先选择,则A B差值最大是多少。

思路:

http://hi.baidu.com/knowledgetime/item/d8ec9420a2b2f98daf48f5a4

第一次做类似的博弈题目,不过还是不难理解的。上面题解还是有一点说的不到位的。

dp[i, j]表示区间[i, j]范围内能选择的最大数。这个最大数不管是A还是B,也就是说可以当作是A B交替的。

由于区间范围是不断扩张的,所以求大区间的最大值时就要向小区间询问,此时小区间相当于是个Oracle,无所不知,只管负责问就行了。

于是对于此题A B可以从两端选取任意多值,所以当A求dp[i, j]时需要询问的区间(而且还要从两端分别)为[i, k],[k+1, j](i <= k < j)

询问的这些子区间可以当作是B所能达到的最大值,使这些B最优值达到最小,即是A的目的。

最后还要考虑一点就是:如果A把区间[i, j]全部取完,还要和上述得到的结果作个比较。

#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <climits>
#include <algorithm>
using namespace std;

const int MAXN = 110;
int dp[MAXN][MAXN];
int a[MAXN], sum[MAXN];

int main()
{
    int n;
    while (scanf("%d", &n) && n)
    {
        sum[0] = 0;
        for (int i = 1; i <= n; ++i)
            scanf("%d", &a[i]), sum[i] = a[i] + sum[i-1];

        for (int i = 1; i <= n; ++i)
            dp[i][i] = a[i];

        for (int p = 2; p <= n; ++p)
        {
            for (int i = 1, j = p; j <= n; ++i, ++j)
            {
                int ans = INT_MIN;
                for (int k = i; k < j; ++k)
                {
                    int m = min(dp[i][k], dp[k+1][j]);
                    m = sum[j] - sum[i-1] - m;
                    if (ans < m)
                        ans = m;
                }
                if (ans < sum[j] - sum[i-1])
                    ans = sum[j] - sum[i-1];
                dp[i][j] = ans;
            }
        }
        printf("%d\n", 2 * dp[1][n] - (sum[n] - sum[0]));
    }
    return 0;
}

 

-------------------------------------------------------

kedebug

Department of Computer Science and Engineering,

Shanghai Jiao Tong University

E-mail: kedebug0@gmail.com

GitHub: http://github.com/kedebug

-------------------------------------------------------

原文地址:https://www.cnblogs.com/kedebug/p/2782192.html