leetcode LCP 07. 传递信息

Posted ZSYL

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了leetcode LCP 07. 传递信息相关的知识,希望对你有一定的参考价值。

题目描述

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

1.有 n 名玩家,所有玩家编号分别为 0 ~ n-1,其中小朋友 A 的编号为 0
2.每个玩家都有固定的若干个可传信息的其他玩家(也可能没有)。传信息的关系是单向的(比如 A 可以向 B 传信息,但 B 不能向 A 传信息)。
3.每轮信息必须需要传递给另一个人,且信息可重复经过同一个人

给定总玩家数 n,以及按 [玩家编号,对应可传递玩家编号] 关系组成的二维数组 relation。返回信息从小 A (编号 0 ) 经过 k 轮传递到编号为 n-1 的小伙伴处的方案数;若不能到达,返回 0。

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

示例 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。

DFS

可以把传信息的关系看成有向图,每个玩家对应一个节点,每个传信息的关系对应一条有向边。

如 x 可以向 y 传信息,则对应从节点 x 到节点 y 的一条有向边。

寻找从编号 0 的玩家经过 k 轮传递到编号 n−1 的玩家处的方案数,等价于在有向图中寻找从节点 0 到节点 n−1 的长度为 k 的路径数,同一条路径可以重复经过同一个节点。

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

具体实现方面,可以对传信息的关系进行预处理,使用列表存储有向边的关系,即可在 O(1) 的时间内得到特定节点的相邻节点(即可以沿着有向边一步到达的节点)。

class Solution {
    // 定义全局变量
    List<List<Integer>> edges;  // 存储有向边的关系
    int n, k, way;
    public int numWays(int n, int[][] relation, int k) {
        this.n = n;
        this.k = k;
        edges = new ArrayList<List<Integer>>();
        // 初始化边
        for(int i = 0; i < n; i++) {
            edges.add(new ArrayList<Integer>());
        }
        // 添加边
        for (int[] edge: relation) {
            int src = edge[0];
            int dst = edge[1];
            edges.get(src).add(dst);
        }
        dfs(0, 0);
        return way;
    }
    public void dfs(int src, int steps) {
        if (steps == k) {
            if (src == n-1)
                way++;
            return;
        }
        for (int nextIndex: edges.get(src)) {
            dfs(nextIndex, steps+1);
        }
    }
}

复杂度分析

  • 时间复杂度:O(n^k)。最多需要遍历 k 层,每层遍历最多有 O(n) 个分支。
  • 空间复杂度: O(n+m+k)。其中m为relation数组长度。空间复杂度主要取决于图的大小和递归调用栈的深度,保存有向图信息所需空间为O(n+m),递归调用栈的深度不会超过 k。

BFS

也可以使用广度优先搜索计算方案数。从节点 0 出发做广度优先搜索,当遍历到 k 层时,如果位于节点 n−1,则将方案数加 1。搜索结束之后,即可得到总的方案数。

class Solution {
    public int numWays(int n, int[][] relation, int k) {
        List<List<Integer>> edges = new ArrayList<List<Integer>>();
        for (int i = 0; i < n; i++) {
            edges.add(new ArrayList<Integer>());
        }
        for (int[] edge : relation) {
            int src = edge[0], dst = edge[1];
            edges.get(src).add(dst);
        }

        int steps = 0;
        Queue<Integer> queue = new LinkedList<Integer>();
        queue.offer(0);
        while (!queue.isEmpty() && steps < k) {
            steps++;
            int size = queue.size();
            for (int i = 0; i < size; i++) {
                int index = queue.poll();
                List<Integer> list = edges.get(index);
                for (int nextIndex : list) {
                    queue.offer(nextIndex);
                }
            }
        }

        int ways = 0;
        // 判断是否步数为k
        if (steps == k) {
            while (!queue.isEmpty()) {
                if (queue.poll() == n - 1) {
                    ways++;
                }
            }
        }
        return ways;
    }
}

复杂度分析

  • 时间复杂度:O(n^k)。最多需要遍历 k 层,每层遍历最多有 O(n) 个分支。
  • 空间复杂度:O(n+m+n^k)。其中m为relation数组的长度。空间复杂度主要取决于图的大小和队列的大小,保存有向图信息所需空间为O(n+m),由于每层遍历最多有O(n)个分支,因此遍历k层时,队列大小O(n ^ k)。

DP

前两种方法都是通过在图中搜索计算方案数。可以换一个思路,这道题是计数问题,可以使用动态规划的方法解决。

定义动态规划的状态 dp[i][j]为经过i轮传递到编号j的玩家的方案数。

由于编号从0开始,当i=0时,一定位于编号0的玩家,不会传递到其他玩家,因此动态规划边界情况下:

在这里插入图片描述
对于传信息的关系 [src, dist],如果第i轮传递编号src的玩家,则第i+1轮可以从 src 传递到 dst 的玩家。因此在计算 dp[i+1][dst],需要考虑可以传递到编号dst的所有玩家。

在这里插入图片描述
最终得到 dp[k][n−1] 即为总的方案数。

class Solution {
    public int numWays(int n, int[][] relation, int k) {
        int[][] dp = new int[k + 1][n];
        dp[0][0] = 1;
        for (int i = 0; i < k; i++) {
            for (int[] edge : relation) {
                int src = edge[0], dst = edge[1];
                dp[i + 1][dst] += dp[i][src];
            }
        }
        return dp[k][n - 1];
    }
}

dp[i][]的值只与dp[i-1][]有关,因此可以把二维数组变为一维数组.

class Solution {
    public int numWays(int n, int[][] relation, int k) {
        int[] dp = new int[n];
        dp[0] = 1;
        for (int i = 0; i < k; i++) {
            int[] next = new int[n];
            for (int[] edge : relation) {
                int src = edge[0], dst = edge[1];
                next[dst] += dp[src];
            }
            dp = next;
        }
        return dp[n - 1];
    }
}

复杂度分析

  • 时间复杂度:O(km)。其中 m 为relation 数组的长度。
  • 空间复杂度:O(n)。

BUG代码:

class Solution {
    public int numWays(int n, int[][] relation, int k) {
        int[][] dp = new int[2][n];
        dp[0][0] = 1;
        for (int i = 0; i < k; i++) {
            for (int[] edge: relation) {
                int src = edge[0];
                int dst = edge[1];
                dp[((i+1)&1)][dst] += dp[(i&1)][src];                
            }
        }
        return dp[1][n-1];
    }
}

以上是关于leetcode LCP 07. 传递信息的主要内容,如果未能解决你的问题,请参考以下文章

LeetCode LCP 07. 传递信息

leetcode LCP.07 传递信息

leetcode LCP.07 传递信息

leetcode LCP 07. 传递信息

LeetCode LCP 07. 传递信息 / NC111 最大数 / NC16 判断二叉树是否对称 / NC13 二叉树的最大深度

文巾解题 LCP 07. 传递信息