dp_hard_1510石子游戏IV

文章目录

  • 题目描述
  • 解析 - 博弈论中的必胜态和必败态
  • Reference

题目描述

Alice 和 Bob 两个人轮流玩一个游戏,Alice 先手。

一开始,有 n 个石子堆在一起。每个人轮流操作,正在操作的玩家可以从石子堆里拿走 任意 非零 平方数 个石子。

如果石子堆里没有石子了,则无法操作的玩家输掉游戏。

给你正整数 n ,且已知两个人都采取最优策略。
如果 Alice 会赢得比赛,那么返回 True ,否则返回 False 。

示例 1:
输入:n = 1
输出:true
解释:Alice 拿走 1 个石子并赢得胜利,因为 Bob 无法进行任何操作。

示例 2:
输入:n = 2
输出:false
解释:Alice 只能拿走 1 个石子,
然后 Bob 拿走最后一个石子并赢得胜利(2 -> 1 -> 0)。

示例 3:
输入:n = 4
输出:true
解释:n 已经是一个平方数,
Alice 可以一次全拿掉 4 个石子并赢得胜利(4 -> 0)。

示例 4:
输入:n = 7
输出:false
解释:当 Bob 采取最优策略时,Alice 无法赢得比赛。
如果 Alice 一开始拿走 4 个石子,
Bob 会拿走 1 个石子,
然后 Alice 只能拿走 1 个石子,
Bob 拿走最后一个石子并赢得胜利(7 -> 3 -> 2 -> 1 -> 0)。
如果 Alice 一开始拿走 1 个石子,
Bob 会拿走 4 个石子,
然后 Alice 只能拿走 1 个石子,
Bob 拿走最后一个石子并赢得胜利(7 -> 6 -> 2 -> 1 -> 0)。

示例 5:
输入:n = 17
输出:false
解释:如果 Bob 采取最优策略,Alice 无法赢得胜利。

提示:
1 <= n <= 10^5

解析 - 博弈论中的必胜态和必败态

首先来看一个比较简单地的问题:
把原问题中的“任意非零平方数个石子”改为 “一个或两个石子”;

很明显当石子堆中剩余一个或者两个石子的时候当前取石子的人必胜(因为两个人都采取最优策略,不可能有人面对两个石子还傻乎乎的取走一个石子),零个石子必败;

那么剩余三个石子时,当前取石子的人可以选择取一个或者两个石子:
1.如果他取走一个石子后,石子堆剩余两个石子,那么那么他就输了,因为接下来取石子的人面对两个石子是必胜的;
2.如果他取走两个石子后,石子堆剩余一个石子,那么他还是输了,因为接下来取石子的人面对一个石子还是必胜的;

此时我们会发现,当面对当前石子堆的人不论采取何种策略都不能置对手于死地,那么他就必败;但是如果他可以采取某种策略(最优策略)使得对手必败,那么他就必胜;最后我们会得到如下的一个树形的图。

dp_hard_1510石子游戏IV_第1张图片

  • 我们定义一个数组 f f f
    • f [ i ] f[i] f[i]表示的是面对当前的 i i i个石子,先手是否必胜
  • 那么我么可以写出此问题的状态转移方程:
    dp_hard_1510石子游戏IV_第2张图片
  • 那么原问题在本质上和这个简单地问题是一致的,我们只需通过一个循环来判断面对当前石子堆的人分别取走 k k k的平方个石子( k = 1 , 2 , 3 , . . . ; k=1,2,3,...; k=1,2,3,...; k k k的平方小于等于 i i i) 后能否置对手于死地,如果可以那么 f [ i ] = t r u e f[i]=true f[i]=true,否则 f [ i ] = f a l s e f[i]=false f[i]=false;
  • python代码
class Solution:
    def winnerSquareGame(self, n: int) -> bool:
        dp = [False]*(n+1)
        dp[1] = True
        for i in range(2, n+1):
            for j in range(1, int(math.sqrt(i))+1):  # 优化:只需遍历到根号i 即可
                if j*j <= i:  # 需要 所有 平方小于i的dp[idx]均为True, dp[i]才为False
                    if not dp[i-j*j]:  # 否则 dp[i] 为 True
                        dp[i] = True
                        break
        # print(dp)
        return dp[n]

  • T i m e : O ( n 1.5 ) Time:O(n^{1.5}) Time:O(n1.5) - 因为内层循环开根号 n \sqrt{n} n n 1 2 n^{\frac{1}{2}} n21次方
  • S p a c e : O ( n ) Space:O(n) Space:O(n)

Reference

Here

你可能感兴趣的:(LeetCode题解)