464. Can I Win(我能赢吗)

464. Can I Win(我能赢吗)

  • Can I Win我能赢吗
    • 题目链接
    • 题目描述
    • 题目分析
      • 方法状态压缩动态规划
        • 算法描述
    • 参考代码

题目链接

https://leetcode.com/problems/can-i-win/description/

题目描述

In the “100 game,” two players take turns adding, to a running total, any integer from 1..10. The player who first causes the running total to reach or exceed 100 wins.

What if we change the game so that players cannot re-use integers?

For example, two players might take turns drawing from a common pool of numbers of 1..15 without replacement until they reach a total >= 100.

Given an integer maxChoosableInteger and another integer desiredTotal, determine if the first player to move can force a win, assuming both players play optimally.

You can always assume that maxChoosableInteger will not be larger than 20 and desiredTotal will not be larger than 300.

Example

Input:
maxChoosableInteger = 10
desiredTotal = 11

Output:
false

Explanation:
No matter which integer the first player choose, the first player will lose.
The first player can choose an integer from 1 up to 10.
If the first player choose 1, the second player can only choose integers from 2 up to 10.
The second player will win by choosing 10 and get a total = 11, which is >= desiredTotal.
Same with other integers chosen by the first player, the second player will always win.

题目分析

这道题是一道典型的状态压缩动态规划题目。所给的问题是NP的,但数据规模较小,利用二进制表示状态,以及动态规划的思想,可以把阶乘的状态压缩为指数的状态。int类型有超过20位的长度,所以可以用一个int表示状态,选择某一个数即把对应位置为1
下面分析几种情况:
- 选择一个可选的数后即可达到或超过desiredTotal时,为必胜状态
- 当选择任意可选的数后均达到必胜状态(说明当前玩家不管选择哪一个数,下一个玩家都是必胜局面,即他必输无疑),为必输状态
- 当选择一个可选的数后,可以达到必输状态(当前玩家一定会选择这个状态,因为下一步他的对手是必输的,这就保证了他的胜利),为必胜状态(题中声明两个玩家每一步都做出最优选择,所以玩家一定会选择能获得胜利的数)

方法:状态压缩动态规划

算法描述

  1. 通过简单计算排除可以直接得到答案的情况:
    • desiredTotal <= 1:直接选择1即可获得胜利
    • 可选数字总和小于desiredTotal:没有人能获得胜利
    • 可选数字总和小于desiredTotalmaxChoosableInteger为奇数时可以获得胜利,否则不能
  2. 利用状态转移方程进行搜索,在搜索中记录状态的输赢情况(1表示必胜,-1表示必输),避免重复计算

参考代码

class Solution {
private:
    int max;
    vector<int> f;

public:
    bool canIWin(int maxChoosableInteger, int desiredTotal) {
        max = maxChoosableInteger;
        f = vector<int>(1 << 20, 0);
        if (desiredTotal <= 1)
            return true;
        int sum = maxChoosableInteger * (maxChoosableInteger + 1) / 2;
        if (sum < desiredTotal)
            return false;
        else if (sum == desiredTotal)
            return maxChoosableInteger % 2;
        else
            return dfs(desiredTotal, 0);
    }

    bool dfs(int total, int state) {
        if (total <= 0)
            return false;
        else if (f[state] > 0)
            return true;
        else if (f[state] < 0)
            return false;
        else {
            for (int i = 0; i < max; i++)
                if (!(state & 1 << i) && !dfs(total - i - 1, state | 1 << i)) {
                    f[state] = 1;
                    return true;
                }
            f[state] = -1;
            return false;
        }
    }
};

你可能感兴趣的:(LeetCode)