[usaco][DP]游戏 A Game

题目梗概

n个数字,A和B每次执行一次动作,动作是可以从左边或者右边选择一个数字加入自己,求两个人都在执行最优策略的情况下,B玩家的获胜次数。

 

思考

显而易见的想法是每次从左端取和右端取数构成最大值,那我可不可以从这个条件入手dp每次是取左端的最大值和取右端的最大值,但是我水平有限。没想出来转移方程。

其次想法是我可以用区间dp的思想,每次维护一个区间的最大值,不断加入新的区间再计算。但是难点在于先后手问题。

在苦于无解的情况下看了下题解的方程,dp[i][j]表示先手在区间[i,j]的最大值。(后手就是总和减去先手的最大值)

sum[i][j]表示区间[i,j]前缀和

$dp[i][j]=max(sum[i][j]-dp[i+1][j],sum[i][j]-dp[i][j-1])$

$dp[i][j]=sum[i][j]-min(dp[i+1][j],dp[i][j-1])$

因为dp[i+1][j]和dp[i][j-1]在dp[i][j]之前就被计算过了,所以可以优化一下空间复杂度 O(n) 时间复杂度O($n^2$)

代码实现

#include <cstdio>
#include <algorithm>

int n,a[202],dp[202],sum[202];

inline int get()
{
    char ch; int res = 0; bool f = true;
    while (((ch = getchar()) < '0' || ch > '9') && ch != '-');
    if (ch == '-') f = false; 
     else res = ch - '0';
    while ((ch = getchar()) >= '0' && ch <= '9')
     res = (res << 3) + (res << 1) + ch - '0';
    return f? res : -res;
}


int main(){
    n = get();
    for(register int i=1;i<=n;++i){
        a[i] = get();
        sum[i]=sum[i-1]+a[i];
        dp[i]=a[i];
    }    
    for(register int len=1;len<n;len++){
        for(register int i=1;i+len<=n;i++){
            dp[i]=sum[i+len]-sum[i-1]-std::min(dp[i],dp[i+1]);
        }
    }
    printf("%d %d",dp[1],sum[n]-dp[1]);
    return 0;
}

 

转载于:https://www.cnblogs.com/OIerLYF/p/7301206.html

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值