leetcode486预测赢家

leetcode486 Predict the Winner

Posted by BY on May 26, 2019

前言

新的一年,好好学习。

正文

问题来源

本问题来自leetcode上的486题。

问题描述

给定一个表示分数的非负整数数组。 玩家1从数组任意一端拿取一个分数,随后玩家2继续从剩余数组任意一端拿取分数,然后玩家1拿,……。每次一个玩家只能拿取一个分数,分数被拿取之后不再可取。直到没有剩余分数可取时游戏结束。最终获得分数总和最多的玩家获胜。
给定一个表示分数的数组,预测玩家1是否会成为赢家。你可以假设每个玩家的玩法都会使他的分数最大化。

示例 1:

示例 1:

输入: [1, 5, 2]
输出: False
解释: 一开始,玩家1可以从1和2中进行选择。
如果他选择2(或者1),那么玩家2可以从1(或者2)和5中进行选择。如果玩家2选择了5,那么玩家1则只剩下1(或者2)可选。
所以,玩家1的最终分数为 1 + 2 = 3,而玩家2为 5。
因此,玩家1永远不会成为赢家,返回 False。

示例 2:

输入: [1, 5, 233, 7]
输出: True
解释: 玩家1一开始选择1。然后玩家2必须从5和7中进行选择。无论玩家2选择了哪个,玩家1都可以选择233。
最终,玩家1(234分)比玩家2(12分)获得更多的分数,所以返回 True,表示玩家1可以成为赢家。

分析:

我的想法是,采用回溯法。相同的一层是自身计算过程,则下一层则代表对手的计算过程。只要能将对手计算逼迫到输,则返回成功否则返回失败;下一层,也就是对手环节也会将自身的对手逼迫到输,若能则返回成功否则返回失败。
如下是我写的代码。

func PredictTheWinner(nums []int) bool {
    return win(nums, 0, len(nums)-1, 0, 0) >= 0
}

func win(nums []int, left, right int, mycount, othercount int) int {
    if left == right {
        if mycount + nums[left] > othercount {
            return 1
        } else if  mycount+nums[left] < othercount {
            return -1
        } else {
            return 0
        }
    }
    ret1 := win(nums, left+1, right, othercount, mycount+nums[left]) 
    if ret1 == -1 {
        return 1
    }
    ret2 := win(nums, left, right-1, othercount, mycount+nums[right]) 
    if ret2 == -1 {
        return 1
    }
    if ret1 == 0 || ret2 == 0 {
        return 0
    }
    return -1
}

网上的递归解法

public class Solution {
    public boolean PredictTheWinner(int[] nums) {
        return helper(nums, 0, nums.length-1) >= 0;
    }
    
    public int helper(int[] nums, int start, int end) {
        if(start == end) return nums[start];
        else return Math.max(nums[start]-helper(nums, start+1,end), nums[end]-helper(nums, start,end-1));
    }
}

网上动态规划解法

  1. 该问题没有直接比较一个选手所拿元素的和值,而是把问题转换为两个选手所拿元素的差值。这一点很巧妙,是关键的一步。
  2. 找出递推表达式:max(nums[beg] - partition(beg + 1, end), nums[end] - partition(beg, end + 1))
  3. 通过递推表达式构造递归算法是比较简单的。但是要构造一个非递归的算法难度较大。对于非递归算法,首先在dp中赋初始值,这是我们解题的第一步。在这个问题中,我们使用一个二位的数组dp来表示nums数组中任意开始和结束位置两人结果的差值。
    初始的时候,我们仅仅知道对角线上的值。dp[i][i] = nums[i]. 这一点很好理解。
    public class Solution {
     public boolean PredictTheWinner(int[] nums) {
         if(nums == null || nums.length == 0) return false;
         int n = nums.length;
         int[][] dp = new int[n][n];
            
         for(int i = 0; i < n; i++) {
             dp[i][i] = nums[i];
         }
            
         for(int i = n-2; i >= 0; i--) {
             for(int j = i+1; j < n; j++) {
                 dp[i][j] = Math.max(nums[i]-dp[i+1][j], nums[j]-dp[i][j-1]);
             }
         }
            
         return dp[0][n-1] >= 0;
     }
    }
    

    总结:

    勤思考。

结语

不管怎么样好好加油。