【LeetCode】一種博弈思路 minimax(共5題)

【292】 Nim Game (2019年3月12日,E)html

有一堆石頭,遊戲規則是每次能夠從裏面拿1-3顆石頭,拿到最後的石頭的人贏。你和你的對手都 optimal 的玩這個遊戲,問先手(也就是你)能不能贏得這個比賽。api

題解:我原本寫的是 dfs + memo,可是沒想到這個題數字太大了。容易爆棧。後來經過看discuss和觀察,發現,這個題,只要不是 4 的倍數,先手都能贏得比賽。less

1 class Solution {
2 public:
3     bool canWinNim(int n) {
4         return n % 4 != 0;
5     }
6 };

  

【375】Guess Number Higher or Lower II (2019年3月12日,google tag,H)ide

給了 1 ~ N 這 N 個數字猜數,我來猜,若是我猜錯了,我猜這個數爲x,那麼我就付 x 元。返回在能保證我贏的狀況下,返回我必須付多少錢。優化

 https://www.1point3acres.com/bbs/thread-197552-1-1.htmlui

 

【464】Can I Win (2019年2月20日,谷歌tag,M)google

給了一個1-100的遊戲,遊戲規則是,兩個選手,player1每次從1-100中抽取一個數字,player2每次也從1-100中抽取一個數字,不能重複抽取相同的數字。直到某個player抽取到一個數,全部輪次中每一個player抽取的數字加和大於等於target(target是提早給定的)。問player1能不能贏這個遊戲。spa

本題的題目意思是把100換成一個變量叫作 maxChoosableInteger。問 player1 能不能贏這個遊戲。code

題解:這是一棵博弈樹的問題。解空間以下圖:htm

裸的dfs搜索方法以下:對於當前的局面,咱們嘗試用一個沒有用過的數做爲當前的解,而後dfs到下一個局面。

若是下個局面返回了false,就是對手不能贏,那我確定很是高興的立刻返回了true,由於這個局面我能贏。(注意返回的時候要把當前局面用過的數字清空。回溯。不能忘。

可是若是下個局面返回了true,我就會認真反思本身,而後後悔當前局面的走法(是的,就是這麼的無賴)去換一種新的走法。

那麼對於當前局面的前一個局面來講,對手看到我返回了true,那他也能夠反悔啊,他心裏os:老子也不這麼走了,我也要換一個走法。因而他就繼續遍歷他解空間的下一個解,直到他在當前空間找到了一個解,這個解能讓他在當前空間贏,或者他找遍了全部的可能解,都贏不了,他只好認輸,"我在當前空間贏不了,我返回false"。

 1 class Solution {
 2 public:
 3     bool canIWin(int maxChoosableInteger, int desiredTotal) {
 4         if (desiredTotal <= 0) {return true;}
 5         if (maxChoosableInteger * (maxChoosableInteger + 1) / 2 < desiredTotal) {return false;}
 6         string used(maxChoosableInteger, '0');
 7         int sum = 0;
 8         return canIWin(maxChoosableInteger, desiredTotal, sum, used);
 9     }
10     bool canIWin(int maxChoosableInteger, int desiredTotal, int sum, string& used) {
11         if (sum >= desiredTotal) {return false;}
12         for (int i = 1; i <= maxChoosableInteger; ++i) {
13             if (used[i-1] == '1') { continue; } //若是當前的數用過了,那麼就換下一個能用的數
14             used[i-1] = '1';
15             if (canIWin(maxChoosableInteger, desiredTotal, sum + i, used) == false) {
16                 //到這裏須要回溯。若是下一個局面爲false的話,就說明當前局面能贏。
17                 //可是你想象一下若是你是一個玩家,當對手告訴你這個局面對手能贏的時候,你會怎麼作,
18                 //確定是返回到一個有一個解-對手不能贏的層次,而後去dfs那個解空間。
19                 used[i-1] = '0'; 
20                 return true;
21             }
22             used[i-1] = '0';
23         }
24         return false;
25     }
26 };
View Code

再加上能夠記憶化或者狀態壓縮的技巧。用個memo。

 1 class Solution {
 2 public:
 3     bool canIWin(int maxChoosableInteger, int desiredTotal) {
 4         if (desiredTotal <= 0) {return true;}
 5         if (maxChoosableInteger * (maxChoosableInteger + 1) / 2 < desiredTotal) {return false;}
 6         string used(maxChoosableInteger, '0');
 7         int sum = 0;
 8         return canIWin(maxChoosableInteger, desiredTotal, sum, used, 0);
 9     }
10     unordered_map<string, bool> memo;
11     bool canIWin(int maxChoosableInteger, int desiredTotal, int sum, string& used, int player) {
12         if (sum >= desiredTotal) {return false;}
13         if (memo.find(used) != memo.end()) {return memo[used];}
14         string key = used;
15         memo[key] = false;
16         for (int i = 1; i <= maxChoosableInteger; ++i) {
17             if (used[i-1] == '1') { continue; }
18             used[i-1] = '1';
19             if (canIWin(maxChoosableInteger, desiredTotal, sum + i, used, 1 - player) == false) {
20                 memo[key] = true;
21                 used[i-1] = '0';
22                 return true;
23             }
24             used[i-1] = '0';
25         }
26         return false;
27     }
28 };
View Code

 

【486】Predict the Winner 

 

【843】Guess the Word (2019年3月12日,google tag)

給了一個 wordlist 猜單詞,給了一個 api ,叫作 int guess(string word),返回咱們的 target 單詞有多少個位置和 word match。

For each test case, you have 10 guesses to guess the word. At the end of any number of calls, if you have made 10 or less calls to master.guess and at least one of these guesses was the secret, you pass the testcase.

題解:這道題不難,其實主要是這種交互式的問題在leetcode上第一次出現。

intuition: 咱們先隨機選一個單詞 word,而後利用返回值 x,若是 target 和 word match 的有 x,那麼咱們只須要選擇在 list 中和 word match x個位置的單詞,這個做爲咱們下次選擇的集合。

這麼作能 ac。

可是能夠優化:若是說咱們隨機選一個單詞,可是若是 返回值 x == 0 的話,那麼其實剩下的解可能不少。咱們試圖找一個單詞,與這個單詞有 0 個match 的單詞解的集合最小就好了。

 1 /**
 2  * // This is the Master's API interface.
 3  * // You should not implement it, or speculate about its implementation
 4  * class Master {
 5  *   public:
 6  *     int guess(string word);
 7  * };
 8  */
 9 class Solution {
10 public:
11     void findSecretWord(vector<string>& wordlist, Master& master) {
12         for (int t = 0; t < 10; ++t) {
13             int n = wordlist.size();
14             unordered_map<string, int> mp;
15             for (int i = 0; i < n; ++i) {
16                 for (int j = i + 1; j < n; ++j) {
17                     if (calMatch(wordlist[i], wordlist[j]) == 0) {
18                         mp[wordlist[i]]++;
19                     }        
20                 } 
21             }
22             pair<string, int> minmax = make_pair(wordlist[0], 1000);
23             for (auto& p : mp) {
24                 if (p.second < minmax.second) {minmax = p;}
25             }
26             int x = master.guess(minmax.first);
27             if (x == 6) {return;}
28             vector<string> list2;
29             for (auto& w : wordlist) {
30                 if (calMatch(w, minmax.first) == x) {
31                     list2.push_back(w);
32                 }
33             }
34             wordlist = list2;
35         }
36     }
37     int calMatch(string& s, string& t) {
38         int match = 0;
39         for (int i = 0; i < s.size(); ++i) {
40             if (s[i] == t[i]) {match++;}
41         }
42         return match;
43     }
44 };
View Code

 

913】Cat and Mouse

相關文章
相關標籤/搜索