A game on an undirected graph is played by two players, Mouse and Cat, who alternate turns.
The graph is given as follows: graph[a]
is a list of all nodes b
such that ab
is an edge of the graph.
Mouse starts at node 1 and goes first, Cat starts at node 2 and goes second, and there is a Hole at node 0.
During each player's turn, they must travel along one edge of the graph that meets where they are. For example, if the Mouse is at node 1
, it must travel to any node in graph[1]
.
Additionally, it is not allowed for the Cat to travel to the Hole (node 0.)
Then, the game can end in 3 ways:
- If ever the Cat occupies the same node as the Mouse, the Cat wins.
- If ever the Mouse reaches the Hole, the Mouse wins.
- If ever a position is repeated (ie. the players are in the same position as a previous turn, and it is the same player's turn to move), the game is a draw.
Given a graph
, and assuming both players play optimally, return 1
if the game is won by Mouse, 2
if the game is won by Cat, and 0
if the game is a draw.
Example 1:
Input: [[2,5],[3],[0,4,5],[1,4,5],[2,3],[0,2,3]]
Output: 0
Explanation: 4---3---1
| |
2---5
\ /
0
Note:
3 <= graph.length <= 50
- It is guaranteed that
graph[1]
is non-empty. - It is guaranteed that
graph[2]
contains a non-zero element.
這道題是貓抓老鼠的問題,Tom and Jerry 都看過吧,小時候看着笑到肚子疼的一部動畫片,真是經典中的經典。這道題在無向圖上模仿了貓抓老鼠的這一個過程,老鼠位於結點1,貓位於結點2,老鼠的目標是逃回老鼠洞結點0,貓的目標是在老鼠進洞之前抓住它。這里假設貓和老鼠都不是沙雕,都會選擇最優的策略。若老鼠能成功逃回洞里,則返回1;若貓能成功抓到老鼠,則返回2;若誰也不能達到目標,則表示平局,返回0。其實這道題的本質還是一個無向圖的遍歷問題,只不過現在有兩個物體在遍歷,比一般的圖遍歷要復雜一些。假設圖中有n個結點,不論是貓還是老鼠,當各自走完了n個結點時還沒有分出勝負,則表示平局,若一人走一步,則最多有 2n 步。這樣的話每一個狀態實際上是由三個因素組成的:當前步數,老鼠所在結點,和貓所在結點。這里可以用動態規划 Dynamic Programming 來解,使用一個三維的 dp 數組,其中 dp[t][x][y] 表示當前步數為t,老鼠在結點x,貓在結點y時最終會返回的值,均初始化為 -1。要求的其實是起始狀態 dp[0][1][2] 的返回值,但沒法一下子求出,這個起始狀態實際上是要通過其他狀態轉移過來,就比如說是求二叉樹最大深度的遞歸函數,雖然對根結點調用遞歸函數的返回值就是最大深度,但在函數遇到葉結點之前都無法得知深度。先來看一些終止狀態,首先當老鼠到達洞口的時候,此時老鼠贏,返回值是1,即所有 dp[?][0][?] 狀態的返回值都是1。其次,當貓和老鼠處於同一個位置時,表示貓抓到老鼠了,此時貓贏,返回值是2,即所有 dp[?][y][y] 狀態的返回值都是2。最后,當走完了 2n 步還沒有分出勝負的話,則是平局,直接返回0即可。
理清了上面的思路,其實代碼就不難寫了,這里使用遞歸的寫法,在遞歸函中,首先判斷步數是否到了 2n,是的話直接返回0;否則判斷x和y是否相等,是的話當前狀態賦值為2並返回;否則再判斷x是否等於0,是的話當前狀態賦值為1並返回。若當前狀態的 dp 值不是 -1,則表示之前已經更新過了,不需要重復計算了,直接返回即可。否則就要來計算當前的 dp 值,先確定當前該誰走,只要判斷t的奇偶即可,因為最開始步數0的時候是老鼠先走。若此時該老鼠走了,它能走的相鄰結點可以在 graph 中找到,對於每一個可以到達的相鄰結點,都調用遞歸函數,此時步數是 t+1,老鼠位置為相鄰結點,貓的位置不變。若返回值是1,表示老鼠贏,則將當前狀態賦值為1並返回;若返回狀態是2,此時不能立馬返回貓贏,因為老鼠可以不走這個結點;若返回值是0,表示老鼠走這個結點是有平局的機會,但老鼠還是要爭取贏的機會,所以此時用一個 bool 變量標記下貓肯定贏不了,但此時也不能直接返回,因為 Jerry 一直要追尋贏的機會。直到遍歷完了所有可能性,老鼠最終還是沒有贏,則看下之前那個 bool 型變量 catWin,若為 true,則標記當前狀態為2並返回,反之,則標記當前狀態為0並返回。若此時該貓走了,基本跟老鼠的策略相同,它能走的相鄰結點也可以在 graph 中找到,對於每一個可以到達的相鄰結點,首先要判斷是否為結點0(老鼠洞),因為貓是不能進洞的,所以要直接跳過這個結點。否則就調用遞歸函數,此時步數是 t+1,老鼠位置不變,貓的位置為相鄰結點。若返回值是2,表示貓贏,則將當前狀態賦值為2並返回;若返回狀態是1,此時不能立馬返回老鼠贏,因為貓可以不走這個結點;若返回值是0,表示貓走這個結點是有平局的機會,但貓還是要爭取贏的機會,所以此時用一個 bool 變量標記下老鼠肯定贏不了,但此時也不能直接返回,因為 Tom 也一直要追尋贏的機會。直到遍歷完了所有可能性,貓最終還是沒有贏,則看下之前那個 bool 型變量 mouseWin,若為 true,則標記當前狀態為1並返回,反之,則標記當前狀態為0並返回,參見代碼如下:
class Solution {
public:
int catMouseGame(vector<vector<int>>& graph) {
int n = graph.size();
vector<vector<vector<int>>> dp(2 * n, vector<vector<int>>(n, vector<int>(n, -1)));
return helper(graph, 0, 1, 2, dp);
}
int helper(vector<vector<int>>& graph, int t, int x, int y, vector<vector<vector<int>>>& dp) {
if (t == graph.size() * 2) return 0;
if (x == y) return dp[t][x][y] = 2;
if (x == 0) return dp[t][x][y] = 1;
if (dp[t][x][y] != -1) return dp[t][x][y];
bool mouseTurn = (t % 2 == 0);
if (mouseTurn) {
bool catWin = true;
for (int i = 0; i < graph[x].size(); ++i) {
int next = helper(graph, t + 1, graph[x][i], y, dp);
if (next == 1) return dp[t][x][y] = 1;
else if (next != 2) catWin = false;
}
if (catWin) return dp[t][x][y] = 2;
else return dp[t][x][y] = 0;
} else {
bool mouseWin = true;
for (int i = 0; i < graph[y].size(); ++i) {
if (graph[y][i] == 0) continue;
int next = helper(graph, t + 1, x, graph[y][i], dp);
if (next == 2) return dp[t][x][y] = 2;
else if (next != 1) mouseWin = false;
}
if (mouseWin) return dp[t][x][y] = 1;
else return dp[t][x][y] = 0;
}
}
};
Github 同步地址:
https://github.com/grandyang/leetcode/issues/913
參考資料:
https://leetcode.com/problems/cat-and-mouse/
[LeetCode All in One 題目講解匯總(持續更新中...)](https://www.cnblogs.com/grandyang/p/4606334.html)