[leetcode flip game ii]sprague–grundy theorem

I learn this theorem from the discuss post of problem flip game ii
The discuss post is here: Theory matters - from Backtracking(128ms) to DP (0ms)

SG theorem is used to determine how to force a win for impartial game. The theory is based on the study of game: the game of nim, which two players alternate taking off any number of stones from a pile until there are no stones left on the heaps.

normal play : the player makes the last move wins.
misere play : the player makes the last move loses.

Typically, normal play is discussed here: the player take the last stones wins.

Example 1:

3 Heaps             Moves
A B C
3 4 5               I take 3 from heap A
0 4 5               You take 2 from heap B
0 2 5               I take 5 from heap C
0 2 0               You take 2 from heap B and win!
0 0 0               (0, 0, 0) is a p-position here!

So how to guarantee a win that you take the final move?

Type of impartial game positions:
P-position: secure a win for previous player.
N-position: secure a win for next player

Backwards induction: determine whether a position in game is a P or N.

  1. terminal position as P.
  2. positions that can reach P as N.
  3. positions that only move to N, label P.

Example 2:
subtraction game: only one heap, players alternate taking off stones of any number Si , from the set S=[1,3,4] , the player take the last chip loses.

x=  0   1   2   3   4   5   6   7   8   9   10  11  12  13  14
pos P   N   P   N   N   N   N   P   N   P   N   N   N   N   P

Solution to nim game:

Theorem: the winning strategy in normal play Nim is to finish every move with XOR of every heap is 0(finish every move to P-position).
The terminal position of 3-heap nim game is: [0, 0, 0], which is a P-position. So the strategy here is on your turn, move the game to P-position, on each turn your opponent will move from P-position to N-position(Definition). Since terminal is P-position, you could guarantee a win for this game.

From nim game, other impartial games could be solved similarly. To further analyze impartial games, we will put the games to graph:

A game consists of a graph G = (X, F) where
X is the set of all possible game positions
F is a function that gives for each xX, a subset of possible x is to move to, called followers. If F(x) is empty, the position x is terminal.
• The start position is x0 ∈ X. So player 1 moves first from x0 .
• Players alternate moves. At position x, the player chooses from yF(x) .
• The player confronted with the empty set F(x) loses.

Example: use the subtraction game in last heap:
subtraction game: only one heap, players alternate taking off stones of any number Si , from the set S=[1,3,4] , the player take the last chip loses.
[leetcode flip game ii]sprague–grundy theorem_第1张图片

Sprague-Grundy function:

g(x)=min{n0:ng(y)foryF(x)}

In other words, SG is the smallest non-negative value not found on the SG values of its followers, which is the same as mex function.
mex({0,1,2,4})=3

Example: the SG values of positions in subtraction game.
[leetcode flip game ii]sprague–grundy theorem_第2张图片

Here comes the last part:
Sprague-Grundy Theorem: the SG function for a sum of games on a graph is just the Nim sum of the SG functions of its components.

The sum of games here is just tow smaller games combined to make a big game. For example, 3 pile Nim is just the sum of 3 games of 1 pile Nim.

Leetcode problem with SG theorem:
1. flip game ii
You are playing the following Flip Game with your friend: Given a string that contains only these two characters: + and -, you and your friend take turns to flip two consecutive “++” into “–”. The game ends when a person can no longer make a move and therefore the other person will be the winner.

Write a function to determine if the starting player can guarantee a win.

For example, given s = “++++”, return true. The starting player can guarantee a win by flipping the middle “++” to become “+- -+”.

The solution is a bottom-up approach for this problem. First get all possible positions’ SG values, then use SG theorem to get the SG value for the initial position.

class Solution 
{ 
private: 
    int mex(unordered_set<int> &followers) 
    { 
        int m = followers.size(); 
        for (int i = 0; i <= m; ++i) 
            if (followers.count(i) == 0) return i; 
    } 
public: 
    bool canWin(string s) 
    { 
        int max_len = 0; 
        vector<int> init;
        // get game position
        for (int i = 0, curr_len = 0; i < s.length(); ++i) 
        { 
            if (s[i] == '+') 
                ++curr_len; 
            if (s[i] == '-' || i == s.length() - 1) 
            { 
                if (curr_len >= 2) 
                    init.push_back(curr_len); 
                max_len = max(max_len, curr_len); curr_len = 0; 
            } 
        }
        // SG function for values from 0 to max_len
        vector<int> g(max_len + 1, 0); 
        for (int i = 2; i <= max_len; ++i) 
        { 
            unordered_set<int> followers; 
            for (int first_game = 0; first_game < i / 2; ++first_game) 
                followers.insert(g[first_game] ^ g[i - first_game - 2]); 
            g[i] = mex(followers); 
        } 
        // get SG value for this game
        int g_init = 0; 
        for_each(init.begin(), init.end(), [&](int sz) { g_init ^= g[sz]; }); 
        // if the initial position's SG value isn't zero, we
        // could guarantee a win by moving the game to position
        // with SG value of 0.
        return g_init != 0; 
    } 
};

2.Can I Win
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.

// the goal to this problem is to determine the initial game is
// in P or N position
class Solution 
{
private:
    unordered_map<int, bool> dict;
    vector<bool> used;
    // determine whether a game position is P-position or
    // N position, return true if it is a N-position.
    bool helper(int d)
    {
        if (d <= 0) return false;
        int key = format();
        if (!dict.count(key))
        {
            for (int i = 1; i < used.size(); ++i)
            {
                if (!used[i])
                {
                    used[i] = true;
                    // a follower is in P-position
                    // thus current is in N-position
                    if (!helper(d - i))
                    {
                        used[i] = false;
                        return dict[key] = true;
                    }
                    used[i] = false;
                }
            }
            // all followers are in N-position, thus current game is 
            // in P-position.
            dict[key] = false;
        }
        return dict[key];
    }
    // transform used array to an int, use int number
    // to represent the game position info.
    int format()
    {
        int ans = 0;
        for (auto b : used)
        {
            ans <<= 1;
            if (b) ans |= 1;
        }
        return ans;
    }
public:
    bool canIWin(int m, int d)
    {
        int sum = (1 + m) * m / 2;
        if (sum < d) return false;
        if (d <= 0) return true;
        used = vector<bool>(1 + m, false);
        return helper(d);
    }
};

你可能感兴趣的:(leetcode,SG-value,Impartial,Leetcode)