博弈问题

你和你的朋友面前有一排石头堆,用一个数组piles表示,piles[i] 表示第 i 堆石子有多少个。你们轮流拿石头,一次拿一堆,但是只能拿走最左边或者最右边的石头堆。所有石头被拿完后,谁拥有的石头多,谁获胜。
石头的堆数可以是任意正整数,石头的总数也可以是任意正整数,这样就能打破先手必胜的局面了。比如有三堆石头 piles = {1, 100, 3},先手不管拿1还是拿3,能够决定胜负的 100 都会被后手拿走,后手会获胜。
假设两人都很聪明,请你设计一个算法,返回先手和后手的最后得分(石头总数)之差。比如上面的那个例子,先手能获得 4 分,后手会获得 100 分,你的算法应该返回 -96。

1、确定dp数组及其下标的含义

dp数组是一个Pair类型的数组,里面包含 first 和 second 两个属性,简写为 fir 和 sec。其实本题也可以用一个三维数组 dp[n][n][2]。
在这里插入图片描述
注:由上述dp数组定义可以看出,j 是大于等于 i 的。因此第二层for循环的 j 是从 j = i 开始的。

2、确定递推公式
图示
3、dp数组如何初始化
图示
4、确定遍历顺序

由递推公式可以看出,要求出 dp[i][j] 需要先求出 dp[i + 1][j],因此 i 需要从下向上遍历;要求出 dp[i][j] 需要先求出 dp[i][j - 1],因此 j 需要从左向右遍历

5、举例推导dp数组
图示

public class BoYi{
    public static void main(String[] args) {
        int[] piles = {3, 9, 1, 2};
        int n = piles.length;
        /* dp[i][j].first表示,对于piles[i...j]这部分石头堆,先手能获得的最高分数。
        dp[i][j].second表示,对于piles[i...j]这部分石头堆,后手能获得的最高分数。
        其实直接用一个三维数组dp[n][n][2]也可以。*/
        Pair[][] dp = new Pair[n][n];
        // 初始化dp数组
        for(int i = 0; i < n; i++){
            for(int j = 0; j < n; j++){
                dp[i][j] = new Pair(0, 0);
            }
        }
        // 填入base case。只有一堆石头,显然先手得分为piles[i],后手的得分为0
        for(int i = 0; i < n; i++){
            dp[i][i].first = piles[i];
            dp[i][i].second = 0;
        }

        for(int i = n - 1; i >= 0; i--){
            for(int j = i; j < n; j++){
                if(i == j){
                    continue;
                }
                int left = piles[i] + dp[i + 1][j].second;
                int right = piles[j] + dp[i][j - 1].second;
                if(left > right){
                    dp[i][j].first = left;
                    dp[i][j].second = dp[i + 1][j].first;
                }else {
                    dp[i][j].first = right;
                    dp[i][j].second = dp[i][j - 1].first;
                }
            }
        }
        
        Pair res = dp[0][n - 1];
        System.out.println(res.first - res.second);
    }
}

class Pair {
    int first;
    int second;

    public Pair(int first, int second) {
        this.first = first;
        this.second = second;
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

静波波呀

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值