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

901 篇文章 209 订阅

作者: 负雪明烛
id: fuxuemingzhu
个人博客: http://fuxuemingzhu.cn/


题目地址:https://leetcode.com/problems/cat-and-mouse/description/

题目描述

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.

题目大意

猫鼠游戏。

有一张无向图,包含最多50个结点。有两个玩家(Mouse和Cat)在图上,Mouse的起始位置是1,Cat的起始位置是2,0处有一个洞,Cat不能移动到0。Mouse和Cat在图上轮流移动,每次必须移动到与当前结点相邻的一个结点。

游戏有三种结束的可能:

  1. Cat和Mouse进入同一结点,则Cat胜利
  2. Mouse进入0结点,则Mouse胜利
  3. Cat和Mouse的位置和回合发生了重复,则平局

问:如果Cat和Mouse都以最优策略行动,最后的结果是什么?

解题方法

这个题实在太难了,我只能参考别人的做法了。正确的做法应该是BFS,而且是已知结果倒着求过程的BFS。

设计节点状态是(m,c,turn),用color[m][c][turn]来记忆该状态的输赢情况.

首先我们将所有已知的确定状态加入一个队列.已知状态包括(0,c,turn)肯定是老鼠赢,(x,x,turn)且x!=0肯定是猫赢。我们尝试用BFS的思路,将这些已知状态向外扩展开去.

扩展的思路是:从队列中取出队首节点状态(m,c,t),找到它的所有邻接的parent的状态(m2,c2,t2).这里的父子关系是指,(m2,c2,t2)通过t2轮(老鼠或猫)的操作,能得到(m,c,t).我们发现,如果(m,c,t)是老鼠赢而且t2是老鼠轮,那么这个(m2,c2,t2)一定也是老鼠赢.同理,猫赢的状态也类似.于是,我们找到了一种向外扩展的方法.

向外扩展的第二个思路是:对于(m2,c2,t2),我们再去查询它的所有children(必定是对手轮)是否都已经标注了赢的状态.如果都是赢的状态,那么说明(m2,c2,t2)无路可走,只能标记为输的状态.特别注意的是,第一条规则通过child找parent,和第二条规则通过parent找child的算法细节是不一样的,一定要小心.

这样我们通过BFS不断加入新的探明输赢的节点.直到队列为空,依然没有探明输赢的节点状态,就是平局的意思!

最后输出(1, 2, MOUSE)的颜色。没有被染过色说明是平局。

时间复杂度是O(VE),空间复杂度是O(V).

class Solution(object):
    def catMouseGame(self, graph):
        """
        :type graph: List[List[int]]
        :rtype: int
        """
        N = len(graph)
        MOUSE, CAT = 1, 2
        # mouse, cat, turn
        color = [[[0] * 3 for i in range(N)] for j in range(N)]
        q = collections.deque()
        for i in range(1, N):
            for t in range(1, 3):
                color[0][i][t] = 1
                q.append((0, i, t))
                color[i][i][t] = 2
                q.append((i, i, t))
        while q:
            curStatus = q.popleft()
            cat, mouse, turn = curStatus
            for preStatus in self.findAllPrevStatus(graph, curStatus):
                preCat, preMouse, preTurn = preStatus
                if color[preCat][preMouse][preTurn] != 0:
                    continue
                if color[cat][mouse][turn] == 3 - turn:
                    color[preCat][preMouse][preTurn] = preTurn
                    q.append(preStatus)
                elif self.allNeighboursWin(color, graph, preStatus):
                    color[preCat][preMouse][preTurn] = 3 - preTurn
                    q.append(preStatus)
        return color[1][2][1]
    
    def findAllPrevStatus(self, graph, curStatus):
        ret = []
        mouse, cat, turn = curStatus
        if turn == 1:
            for preCat in graph[cat]:
                if preCat == 0:
                    continue
                ret.append((mouse, preCat, 2))
        else:
            for preMouse in graph[mouse]:
                ret.append((preMouse, cat, 1))
        return ret
    
    def allNeighboursWin(self, color, graph, status):
        mouse, cat, turn = status
        if turn == 1:
            for nextMouse in graph[mouse]:
                if color[nextMouse][cat][2] != 2:
                    return False
        elif turn == 2:
            for nextCat in graph[cat]:
                if nextCat == 0:
                    continue
                if color[mouse][nextCat][1] != 1:
                    return False
        return True

参考资料

https://zhanghuimeng.github.io/post/leetcode-913-cat-and-mouse/
https://github.com/wisdompeak/LeetCode/tree/master/BFS/913.Cat-and-Mouse

日期

2018 年 10 月 24 日 —— 程序员节被严重炒作了啊

你好!对于LeetCode上的问题994.腐烂的橘子,你可以使用Python来解决。下面是一个示例代码: ```python from collections import deque def orangesRotting(grid): # 记录网格的行数和列数 row, col = len(grid), len(grid[0]) # 定义四个方向:上、下、左、右 directions = [(-1, 0), (1, 0), (0, -1), (0, 1)] # 使用队列来保存腐烂的橘子的位置 queue = deque() # 记录新鲜橘子的数量 fresh_count = 0 # 遍历整个网格,初始化队列和新鲜橘子的数量 for i in range(row): for j in range(col): if grid[i][j] == 2: # 腐烂的橘子 queue.append((i, j)) elif grid[i][j] == 1: # 新鲜橘子 fresh_count += 1 # 如果新鲜橘子的数量为0,直接返回0 if fresh_count == 0: return 0 # 初始化分钟数 minutes = 0 # 开始进行BFS,直到队列为空 while queue: # 记录当前分钟数下,队列中的元素数量 size = len(queue) # 遍历当前分钟数下的所有腐烂的橘子 for _ in range(size): x, y = queue.popleft() # 遍历四个方向 for dx, dy in directions: nx, ny = x + dx, y + dy # 判断新位置是否在网格内,并且是新鲜橘子 if 0 <= nx < row and 0 <= ny < col and grid[nx][ny] == 1: # 将新鲜橘子变为腐烂状态 grid[nx][ny] = 2 # 将新鲜橘子的位置加入队列 queue.append((nx, ny)) # 新鲜橘子的数量减1 fresh_count -= 1 # 如果当前分钟数下,没有新鲜橘子了,结束循环 if fresh_count == 0: break # 每遍历完一层,分钟数加1 minutes += 1 # 如果最后还有新鲜橘子,返回-1,否则返回分钟数 return -1 if fresh_count > 0 else minutes ``` 你可以将给定的网格作为参数传递给`orangesRotting`函数来测试它。请注意,该代码使用了BFS算法来遍历橘子,并计算腐烂的分钟数。希望能对你有所帮助!如果有任何疑问,请随时问我。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值