486. Predict the Winner

今天遇到一道很有趣的DP题,参考了MIT教授的思路

很显然,这道题current situation depends on another (the next) step’s result,适合用dynamic programming解决,但找出dp公式并不简单。

因为我们有两个gamer,想知道第一个出手的gamer A能不能赢,则希望A每次出手都是在通向获得maximum score的道路上的最优选择;同时必须要假设第二个出手的gamer B肯定也是遵循同样的策略,希望每次出手都能在最终最大化地打击到A,使得A获得minimum score(关键点)。

在这里使用二维dp,dp[i][j]的意义为:当score array中,还剩下index区间为[i,j]的这些score没有被pick时,A能获得的maximum score;此时,index区间在[0,i-1][j+1,n-1]的score都已经被A或B pick走了。那么,A由此开始的第一轮只能pick ij,如果选择i,留给B pick的区间变为[i+1,j],如果选择j,留给B pick的区间变为[i,j-1],则有:
dp[i][j] = Math.max(nums[i] + 「第二轮B pick i + 1 或 j后A在第三轮能获得的maximum score」, nums[j] + 「第二轮B pick i 或 j - 1后A在第三轮能获得的maximum score」)

两个「」中的部分怎么计算呢?那需要分情况来看:

第一轮A的选择第二轮B的选择第三轮A的maximum score
ii+1dp[i+2][j]
ijdp[i+1][j-1]
jidp[i+1][j-1]
jj-1dp[i][j-2]

刚刚说过,B也会遵循同样的找最优策略,试图使A在第三轮获得的score最小,导致A在第三轮能获得的maximum score为Math.min(dp[i+2][j], dp[i+1][j-1])Math.min(dp[i+1][j-1], dp[i][j-2])。则有最终公式:
dp[i][j] = Math.max(nums[i] + Math.min(dp[i+2][j], dp[i+1][j-1]), nums[j] + Math.min(dp[i+1][j-1], dp[i][j-2]))

代码如下:

public boolean PredictTheWinner(int[] nums) {
        int n = nums.length;
        if (n % 2 == 0) return true; //注
        int[][] dp = new int[n][n];
        int sum = 0;
        for(int i = 0; i < n; i++) {
            dp[i][i] = nums[i];
            sum += nums[i];
        }
        
        for(int j = 1; j < n; j++) {
            for(int i = j - 1; i >= 0; i--) {
                int a = (i + 1 < n && j - 1 >= 0) ? dp[i + 1][j - 1] : 0;
                int b = (i + 2 < n) ? dp[i + 2][j] : 0;
                int c = (j - 2 >= 0) ? dp[i][j - 2] : 0;
                dp[i][j] = Math.max(Math.min(a,b) + nums[i], Math.min(a,c) + nums[j]);
            }
        }
        return dp[0][n-1] * 2 >= sum;
    }

注:此处为优化,前提假设是两个gamer开始游戏之前能看到所有score。当score array长度为偶数时,index为奇数的score之和与index为偶数的score之和,必有相对较大的一个;而每次pick头或者尾的规则,能保证A在每一轮无论B的选择如何,都能pick和较大的index奇数组或index偶数组的所有元素(因为A首先pick或B pick完后,头或尾总是一奇一偶的),使得最终score之和比B大。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值