UVA 10891 Game of Sum (博弈 + 区间dp)

来源:互联网 发布:谁与知同梅飘雪 编辑:程序博客网 时间:2024/05/24 15:40

题意:

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

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

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

思路:

对于此数列,假如面对的是(i,j)这个区间。(i,j 代表第i、j个数)
dp(i,j)代表先手所得到的最大值。
但对于这个决策,我们需要他的分成两份的子区间的信息。(比如【1,4】可以分成【1,1】【2,4】或者【1,2】【3,4】或者【1,3】【4,4】)
因为要取得的是最大的,所以我们只要知道他分成的子区间取得的最小值(相当于后手选最小值),再用sum【i,j】减去这个值就是正解了。
答案为A-B的得分,所以ans=dp(1,n)- (sum(1,n)-dp(1,n))= 2 * dp(1,n) - sum(1,n)。

我这份代码里的关键是

mem[i][j] = max(sum[j]-sum[i-1],mem[i][j]); //全取的情况。for(int k = i;k < j;k++){//分成两份的情况。    mem[i][j] = max(mem[i][j],sum[j]-sum[i-1]-min(dp(i,k),dp(k+1,j)));}

AC代码:

#include <iostream>#include <cstdio>#include <string.h>#define INF 0x3f3f3f3fusing namespace std;int a[110];int mem[110][110];bool vis[110][110];int sum[110];int dp(int i,int j){    if(vis[i][j]) return mem[i][j];    mem[i][j] = max(sum[j]-sum[i-1],mem[i][j]);    for(int k = i;k < j;k++){        mem[i][j] = max(mem[i][j],sum[j]-sum[i-1]-min(dp(i,k),dp(k+1,j)));    }    vis[i][j] = true;    return mem[i][j];}int main(){    int n;    while(scanf("%d",&n),n){        memset(mem,-0x3f3f3f3f,sizeof(mem));        memset(vis,false,sizeof(vis));        for(int i = 1;i <= n;i++){            scanf("%d",a+i);            mem[i][i] = a[i];            vis[i][i] = true;        }        sum[0] = 0;        for(int i = 1;i <= n;i++){            sum[i] = sum[i-1] + a[i];        }        printf("%d\n",2*dp(1,n)-sum[n]);    }    return 0;}
1 0
原创粉丝点击