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

题意:

给定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 0x3f3f3f3f
using 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
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值