[Leetcode] 传递信息(有向图路径)

小朋友 A 在和 ta 的小伙伴们玩传信息游戏,游戏规则如下:

有 n 名玩家,所有玩家编号分别为 0 ~ n-1,其中小朋友 A 的编号为 0

每个玩家都有固定的若干个可传信息的其他玩家(也可能没有)。传信息的关系是单向的。每轮信息必须需要传递给另一个人,且信息可重复经过同一个人。

给定总玩家数 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]

题目链接:https://leetcode.cn/problems/chuan-di-xin-xi/

参考题解链接:https://leetcode.cn/problems/chuan-di-xin-xi/solution/chuan-di-xin-xi-by-leetcode-solution/

可以通过深度优先搜索、广度优先搜索或动态规划实现

    • 深度优先搜索

从节点 0 出发做深度优先搜索,每一步记录当前所在的节点以及经过的轮数,当经过 k 轮时,如果位于节点 n-1,则将方案数加 1。搜索结束后可得到总的方案数。

class Solution:
    def numWays(self, n: int, relation: List[List[int]], k: int) -> int:
        # 将 某节点-该节点能到达的节点列表 存成字典
        edges = collections.defaultdict(list)
        for src, dst in relation:
            edges[src].append(dst)
        
        ways = 0
        #深度优先搜索方法,参数 到达的节点node,经过了多少步 steps
        def dfs(node, steps):
            #如果步数为k,判断所在节点是否为n-1,是则所求路径数加一
            if steps == k:
                if node == n - 1:
                    nonlocal ways
                    ways += 1
                return
            for nextNode in edges[node]:
                dfs(nextNode,steps+1)

        #从节点 0 出发做深度优先搜索      
        dfs(0,0)
        return ways 

上述深度优先搜索作为内部函数,也可以拿出来作为一个方法,如下,

class Solution:
    def numWays(self, n: int, relation: List[List[int]], k: int) -> int:
        self.ways , self.n, self.k = 0, n, k
        # 将 某节点-该节点能到达的节点列表 存成字典
        self.edges = collections.defaultdict(list)
        for src, dst in relation:
            self.edges[src].append(dst)

        #从节点 0 出发做深度优先搜索
        self.dfs(0,0)
        return self.ways 

    #深度优先搜索方法,参数 到达的节点node,经过了多少步 steps
    def dfs(self, node, steps):
        #如果步数为k,判断所在节点是否为n-1,是则所求路径数加一
        if steps == self.k:
            if node == self.n - 1:
                self.ways += 1
            return
        for nextNode in self.edges[node]:
            self.dfs(nextNode,steps+1)

2. 广度优先搜索

从节点 0 出发做广度优先搜索,当遍历到 k层时,如果位于节点 n-1,则将方案数加 1。

class Solution:
    def numWays(self, n: int, relation: List[List[int]], k: int) -> int:
        # 将 某节点-该节点能到达的节点列表 存成字典
        edges = collections.defaultdict(list)
        for src, dst in relation:
            edges[src].append(dst)
        steps = 0
        ways = 0
        # 初始化一个队列,该队列里只有初始节点0
        queue = collections.deque([0])

        # 当走的步数小于k时/遍历的层数小于k时,把每层的节点逐个拎出来,再用一层循环找到每个节点的所有可能的下一个节点,append到队列,做完这个操作相当于往前走了一步(层),steps 加一
        while queue and steps < k:
            for i in range(len(queue)):
                node = queue.popleft()

                for nextNode in edges[node]:
                    queue.append(nextNode)
                
            steps += 1
        #遍历到第k层,即走了k步,不满足上述循环条件,此时steps为 k,queue中是第k层的目标节点,即全部路径走到第k步所在的节点
        if steps == k:
            for node in queue:
                #如果此时所在的节点是n-1,所求路径数加一
                if node == n-1:
                    ways += 1
        return ways
            

3. 动态规划

定义动态规划的状态 dp[i][j] 为经过 i 轮传递到节点 j 的方案数,其中 0<=i<=k,0<=j<=n-1。

如果第 i 轮传递到编号 src 的玩家,则第 i+1 轮可以从编号 src 的玩家传递到编号 dst 的玩家。

动态规划的状态转移方程:

class Solution:
    def numWays(self, n: int, relation: List[List[int]], k: int) -> int:
        #初始化一个k+1行,n列的二维数组
        dp = [[0] * n for _ in range(k + 1)]

        #当 i=0时,一定位于编号 0的玩家,j!=0 时,dp[0][j] = 0 ,保持初始化的0即可
        dp[0][0] = 1
        for i in range(k):
            for src,dst in relation:
                dp[i + 1][dst] += dp[i][src]
        # 返回经过 k 轮传递到节点 n-1 的方案数
        return dp[k][n - 1]
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值