Uva 10891 经典博弈区间DP

经典博弈区间DP

题目链接:https://uva.onlinejudge.org/external/108/p10891.pdf

题意:

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

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

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

分析:

总和是一定的,所以一个得分越高,另一个人的得分越低。当前状态总是最开始的状态的一个子状态。

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)

0表示全部取完;

答案就是 d(1,n) - ( sum(1,n) - d(1,n) );

tip: sum(i,j) 可以在 O(1) 的时间内求出来。 S[i] 是 1~ i 的和,那么 sum(i,j) = S[j] - S[i-1];

#include <bits/stdc++.h>

using namespace std;

int n;
const int maxn = 100 + 10;

int S[maxn],A[maxn],d[maxn][maxn],vis[maxn][maxn];

int dp(int i,int j) {
    if(vis[i][j])
        return d[i][j];
    vis[i][j] = 1;

    int m = 0;
    for(int k=i+1;k<=j;k++)
        m = min(m,dp(k,j));
    for(int k=i;k<j;k++)
        m = min(m,dp(i,k));

    d[i][j] = S[j] - S[i-1] - m;
    return d[i][j];

}

int main()
{
    while(scanf("%d",&n)==1&&n) {
        S[0] = 0;
        for(int i=1;i<=n;i++) {
            scanf("%d",&A[i]);
            S[i] = S[i-1] + A[i];
        }
        memset(vis,0,sizeof(vis));
        printf("%d
",2*dp(1,n)-S[n]);
    }
    return 0;
}
原文地址:https://www.cnblogs.com/TreeDream/p/6129453.html