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:

  1. 3 <= graph.length <= 50
  2. It is guaranteed that graph[1] is non-empty.
  3. 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/

https://leetcode.com/problems/cat-and-mouse/discuss/176177/Most-of-the-DFS-solutions-are-WRONG-check-this-case

https://leetcode.com/problems/cat-and-mouse/discuss/298937/DP-memory-status-search-search-strait-forward-and-easy-to-understand

[LeetCode All in One 题目讲解汇总(持续更新中...)](https://www.cnblogs.com/grandyang/p/4606334.html)

[LeetCode] 913. Cat and Mouse 猫和老鼠的更多相关文章

  1. 【LeetCode】913. Cat and Mouse 解题报告(Python)

    作者: 负雪明烛 id: fuxuemingzhu 个人博客: http://fuxuemingzhu.cn/ 目录 题目描述 题目大意 解题方法 参考资料 日期 题目地址:https://leetc ...

  2. [Swift]LeetCode913.猫与老鼠 | Cat and Mouse

    A game on an undirected graph is played by two players, Mouse and Cat, who alternate turns. The grap ...

  3. 【LeetCode】代码模板,刷题必会

    目录 二分查找 排序的写法 BFS的写法 DFS的写法 回溯法 树 递归 迭代 前序遍历 中序遍历 后序遍历 构建完全二叉树 并查集 前缀树 图遍历 Dijkstra算法 Floyd-Warshall ...

  4. 【LeetCode】576. Out of Boundary Paths 解题报告(Python)

    作者: 负雪明烛 id: fuxuemingzhu 个人博客: http://fuxuemingzhu.cn/ 目录 题目描述 题目大意 解题方法 动态规划 状态搜索 记忆化搜索 相似题目 参考资料 ...

  5. leetcode hard

    # Title Solution Acceptance Difficulty Frequency     4 Median of Two Sorted Arrays       27.2% Hard ...

  6. Swift LeetCode 目录 | Catalog

    请点击页面左上角 -> Fork me on Github 或直接访问本项目Github地址:LeetCode Solution by Swift    说明:题目中含有$符号则为付费题目. 如 ...

  7. 【LeetCode】一种博弈思路 minimax(共5题)

    [292] Nim Game (2019年3月12日,E) 有一堆石头,游戏规则是每次可以从里面拿1-3颗石头,拿到最后的石头的人赢.你和你的对手都 optimal 的玩这个游戏,问先手(也就是你)能 ...

  8. 【17】有关python面向对象编程的提高【多继承、多态、类属性、动态添加与限制添加属性与方法、@property】

    一.多继承 案例1:小孩继承自爸爸,妈妈.在程序入口模块再创建实例调用执行 #father模块 class Father(object): def __init__(self,money): self ...

  9. python实现类的多态

    多态 关注公众号"轻松学编程"了解更多. 1.多态使用 一种事物的多种体现形式,举例:动物有很多种 注意: 继承是多态的前提 函数重写就是多态的体现形式 演示:重写Animal类 ...

随机推荐

  1. python免安装版(绿色版)制作

    一.实验环境 1.Windows7x64_SP1 二.需求背景 个人编写了一个软件安装器,用于一键安装开发软件及工具,该工具基于python + pywinauto. 但问题来了,新电脑上未安装pyt ...

  2. 自已开发IM有那么难吗?手把手教你自撸一个Andriod版简易IM (有源码)

    本文由作者FreddyChen原创分享,为了更好的体现文章价值,引用时有少许改动,感谢原作者. 1.写在前面 一直想写一篇关于im即时通讯分享的文章,无奈工作太忙,很难抽出时间.今天终于从公司离职了, ...

  3. 解决静态方法调用注入的service

    在使用jpa的复杂查询时,声明了specification时声明为静态方法,导致注入的service无法使用,故想到俩种方式,一种手动注入,一种注解注入,此文使用的时注解注入: 解决静态方法调用注入的 ...

  4. Solr单机版的安装与使用

    .使用Solr实现. 基于Solr实现站内搜索扩展性较好并且可以减少程序员的工作量,因为Solr提供了较为完备的搜索引擎解决方案,因此在门户.论坛等系统中常用此方案. .什么是Solr. Solr是A ...

  5. SpringBoot2版本Caused by: java.sql.SQLSyntaxErrorException: Table 'dinner.hibernate_sequenc

    1.SpringBoot2版本Caused by: java.sql.SQLSyntaxErrorException: Table 'dinner.hibernate_sequenc报错. -java ...

  6. Windows Service 服务搭配FluentScheduler实现定时任务调度

    Windows Service 服务 创建Windows Service 项目 创建一个Windows Service项目,并将项目名称改为 TaskWindowService 在解决方案资源管理器内 ...

  7. Lucene搜索/索引过程笔记

    lucene索引文档过程: > 初始化IndexWriter > 构建Document > 调用IndexWriter.addDocument执行写入 > 初始化Documen ...

  8. 关于css中布局遇到的一些问题

    现在本人初学网页布局经常遇到一些布局问题比如图片错位. 遇到的问题以及解决方案如下 行内元素有缝隙 块级元素没有缝隙 行内块元素中间会有小缝隙    常见的解决办法就是浮动

  9. MES助力日立电梯提升精细化管理水平

    项目背景介绍 日立电梯在2008年到2012年期间分别在五地工厂(上海.广州.天津.成都.扶梯)上线了ERP系统,在后续的使用时间里,逐渐发现现有ERP系统对于生产现场管理,产品质量追溯,产能控制等方 ...

  10. iOS处理含中文的请求链接

    NSString *urlStr = @""; // 将中文URL进行转码 urlStr = [urlStr stringByAddingPercentEscapesUsingEn ...