395. 硬币排成线 II

395. 硬币排成线 II

中文English

有 n 个不同价值的硬币排成一条线。两个参赛者轮流从 左边 依次拿走 1 或 2 个硬币,直到没有硬币为止。计算两个人分别拿到的硬币总价值,价值高的人获胜。

请判定 先手玩家 必胜还是必败?

若必胜, 返回 true, 否则返回 false.

样例

样例 1:

输入: [1, 2, 2]
输出: true
解释: 先手玩家直接拿走两颗硬币即可.

样例 2:

输入: [1, 2, 4]
输出: false
解释: 无论先手拿一个还是两个, 后手可以拿完, 然后总价值更高.
输入测试数据 (每行一个参数)如何理解测试数据?

 解法一:动态规划

dp[i]代表的是倒数n-i个,逆着算,例如dp[length - 2] = values[length - 2] + values[length - 1] ,表示的是i到length - 1可以拿到的最大值

class Solution:
    """
    @param values: a vector of integers
    @return: a boolean which equals to true if the first player will win
    """
    def firstWillWin(self, values):
        # write your code here
        #动态规划
        #如果是len(values)<= 3的情况,则直接给出答案
        #如果是len(valeus) > 3的情况,分情况,要么先手拿一个,要么拿两个
        
        length = len(values)
        if (length <= 2): return True 
        
        dp = [0 for _ in range(length + 1)]
        total = 0 
        
        #如果是<=3的话
        #如果是只拿最后一个的话,先手必拿
        dp[length - 1] = values[length - 1]
        #如果是剩余最后两个的话,先手也必拿
        dp[length - 2] = values[length - 1] + values[length - 2]
        #如果是剩余三个的话,那么先手最优策略是拿前两个,最后一个留给后手
        dp[length - 3] = values[length - 2] + values[length - 3]
        
        total = sum(values[length - 3:])
        #如果是大于3的话,分情况考虑
        for i in range(length - 4, -1, -1):
            total += values[i]
            
            #先手要么拿一个,要么拿两个,后手也是最优策略,所以后手也尽可能的多拿,min(dp[i + 2], dp[i + 3])
            dp[i] = max(values[i] + min(dp[i + 2], dp[i + 3]), values[i] + values[i + 1] + min(dp[i + 3], dp[i + 4]))
        
        #最后比较两者大小,肯定是先手拿,也就是第一个
        return dp[0] > total - dp[0]
            

 

394. 硬币排成线

中文English

有 n 个硬币排成一条线。两个参赛者轮流从右边依次拿走 1 或 2 个硬币,直到没有硬币为止。拿到最后一枚硬币的人获胜。

请判定 先手玩家 必胜还是必败?

若必胜, 返回 true, 否则返回 false.

样例

样例 1:

输入: 1
输出: true

样例 2:

输入: 4
输出: true
解释: 
先手玩家第一轮拿走一个硬币, 此时还剩三个.
这时无论后手玩家拿一个还是两个, 下一次先手玩家都可以把剩下的硬币拿完.

挑战

O(1) 时间复杂度且O(1) 存储。

输入测试数据 (每行一个参数)如何理解测试数据?

 解法:动态规划

class Solution:
    """
    @param n: An integer
    @return: A boolean which equals to true if the first player will win
    """
    def firstWillWin(self, n):
        # write your code here
        #当前必胜和必败由前面两个决定
        #只考虑先手,and表示的是后手的最优策略,只要有前面先手有一个为False,那么后手一定是True
        #dp[i] = dp[i - 2] and dp[i - 3] or dp[i - 3] and dp[i - 4]
        #当前先手 = 取一个后手的最优策略 取两个后手的最优策略(所以中间是and,表示,只要之前先手一个为False,此时后手采取最优策略,一定为True)
        if not n: return False
        if n <= 2: return True 
        
        dp = [False for _ in range(n + 1)]

        #前面三个的话,是固定
        dp[1] = True 
        dp[2] = True
        dp[3] = False 
        
        for i in range(4, n + 1):
            dp[i] = (dp[i - 2] and dp[i - 3]) or (dp[i - 3] and dp[i - 4])
        
        return dp[n]
            

 

posted @ 2020-07-18 21:45  风不再来  阅读(234)  评论(0编辑  收藏  举报