1. 问题描述:
小朋友 A 在和 ta 的小伙伴们玩传信息游戏,游戏规则如下:
有 n 名玩家,所有玩家编号分别为 0 ~ n-1,其中小朋友 A 的编号为 0
每个玩家都有固定的若干个可传信息的其他玩家(也可能没有)。传信息的关系是单向的(比如 A 可以向 B 传信息,但 B 不能向 A 传信息)。
每轮信息必须需要传递给另一个人,且信息可重复经过同一个人
给定总玩家数 n,以及按 [玩家编号,对应可传递玩家编号] 关系组成的二维数组 relation。返回信息从小 A (编号 0 ) 经过 k 轮传递到编号为 n-1 的小伙伴处的方案数;若不能到达,返回 0。
示例 1:
输入:n = 5, relation = [[0,2],[2,1],[3,4],[2,3],[1,4],[2,0],[0,4]], k = 3
输出:3
解释:信息从小 A 编号 0 处开始,经 3 轮传递,到达编号 4。共有 3 种方案,分别是 0->2->0->4, 0->2->1->4, 0->2->3->4。
示例 2:
输入:n = 3, relation = [[0,2],[2,1]], k = 2
输出:0
解释:信息不能从小 A 处经过 2 轮传递到编号 2
限制:
2 <= n <= 10
1 <= k <= 5
1 <= relation.length <= 90, 且 relation[i].length == 2
0 <= relation[i][0],relation[i][1] < n 且 relation[i][0] != relation[i][1]
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/chuan-di-xin-xi
2. 思路分析:
① 从题目中可以知道我们需要将起点0到终点n - 1中间的路径连接起来,使得路径的间隔为k,并且需要求解出所有满足条件条件的路径,所以这是一个路径搜索问题,对于这种从起点出发,中间通过能够到达的点进行联系的求解路径的问题(搜索所有从起点到终点所有路径的问题),经典的做法是使用dfs的思路来解决,因为dfs能够尝试所有的可能性,也就是可以搜索所有的路径,最终得到所有满足题目要求条件的路径
② 使用dfs方法进行求解,首先需要确定方法中的参数,分析题目可以知道我们需要传入能够使用下标访问的图,也就是能够通过下标来找到当前节点可以到达的节点(邻接点),第二个是需要传入记录到达当前的路径的间隔,这样在递归的时候可以根据这个条件进行判断是否满足经过k轮,第三个是需要传入当前正在递归的节点值,这样可以在for循环中进行递归,尝试当前节点下能够到达的所有邻接节点然后再递归下去,最后是题目中n与k的参数,方便递归出口判断是否满足题目条件
③ 创建使用下标能够访问的图,java语言可以使用List集合进行创建,python语言可以使用字典来存储图,python中可以使用collections.defaultdict(list)方法来创建字典,这样键可以表示当前的顶点,值表示当前顶点能够到达的邻接顶点(值的类型是List),使用字典存储能够使用下标进行访问的图在python中是很方便的,dfs在后面搜索的时候就可以直接进行for循环进行搜索
④ 递归的出口:对于递归的出口我们需要判断两个条件,第一个是否满足k轮,第二个当前即将递归的数字是否是n - 1,因为测试用例中有可能是存在环的,比如[[0, 2], [2, 0]],结合题目中给出的传递的信息可以是同一个人的条件我们可以在递归的出口判断一下到达当前数字是第几轮,如果大于等于了k轮那么return,因为这个时候往下递归就没有意义了(使用大于等于了k轮这个条件进行判断的话那么在递归的时候是可以多次访问同一个点的),假如题目中规定了不能够访问之前已经访问过的节点那么我们可以使用一个变量来标记当前节点的父节点,当递归的时候邻接点不等于父节点的时候进行递归
⑤ 我们可以使用一个List来记录中间路径的形成过程,在递归一开始将当前数字加入到路径中,当退回到当前节点的时候将上一次的节点删除掉,这样我们在递归的出口的时候满足条件的可以输出对应的路径
3. 代码如下:
import collections
from typing import List
class Solution:
res = 0
def dfs(self, n: int, relation: dict, k: int, count: int, num: int):
if k == count and num == n - 1:
self.res += 1
return
if count >= k: return
for i in relation[num]:
self.dfs(n, relation, k, count + 1, i)
def numWays(self, n: int, relation: List[List[int]], k: int) -> int:
dic = collections.defaultdict(list)
for cur in relation:
dic[cur[0]].append(cur[1])
self.dfs(n, dic, k, 0, 0)
return self.res
记录路径的代码:
import collections
from typing import List
class Solution:
res = 0
rec = list()
def dfs(self, n: int, relation: dict, k: int, count: int, num: int):
# 记录路径上的节点
self.rec.append(num)
if k == count and num == n - 1:
self.res += 1
print(self.rec)
return
# 这里可以避免有环的时候不停地递归调用导致死循环了
if count >= k: return
for i in relation[num]:
self.dfs(n, relation, k, count + 1, i)
# 弹出递归调用前加入的节点, 这样可以尝试当前节点的下一个邻接点
self.rec.pop()
def numWays(self, n: int, relation: List[List[int]], k: int) -> int:
dic = collections.defaultdict(list)
# 存储可以使用下标访问的图
for cur in relation:
dic[cur[0]].append(cur[1])
self.dfs(n, dic, k, 0, 0)
return self.res