LCP 07. 传递信息
题目:
小朋友 A 在和 ta 的小伙伴们玩传信息游戏,游戏规则如下:
- 有 n 名玩家,所有玩家编号分别为 0 ~ n-1,其中小朋友 A 的编号为 0
- 每个玩家都有固定的若干个可传信息的其他玩家(也可能没有)。传信息的关系是单向的(比如 A 可以向 B 传信息,但 B 不能向 A 传信息)。
- 每轮信息必须需要传递给另一个人,且信息可重复经过同一个人
给定总玩家数 n,以及按 [玩家编号,对应可传递玩家编号] 关系组成的二维数组 relation。返回信息从小 A (编号 0 ) 经过 k 轮传递到编号为 n-1 的小伙伴处的方案数;若不能到达,返回 0。
示例1:
示例 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
leetcode链接:LCP 07. 传递信息
题解:
思路1:dfs
题目中的relation
很容易抽象成有向图,而对于可行路径的查找也很自然地联想到使用深度优先搜索。主要步骤归纳如下:
- 有向图初始化
List<List<Integer>> edges;
edges = new ArrayList<>();
for(int i = 0; i < n; i++){
edges.add(new ArrayList<>());
}
for(int[] edge : relation){
edges.get(edge[0]).add(edge[1]);
}
- 深度优先搜索
private void dfs(int cur, int steps){
if(steps == k){
if(cur == n - 1){
res++;
//System.out.println(res);
}
return;
}
List<Integer> l = edges.get(cur);
for(int next : l){
dfs(next, steps + 1);
}
}
public int numWays(int n, int[][] relation, int k) {
...
dfs(0,0);
}
合并后即可写出最终代码
class Solution {
int res, n, k;
List<List<Integer>> edges;
public int numWays(int n, int[][] relation, int k) {
res = 0;
this.n = n;
this.k = k;
edges = new ArrayList<>();
for(int i = 0; i < n; i++){
edges.add(new ArrayList<>());
}
for(int[] edge : relation){
edges.get(edge[0]).add(edge[1]);
}
dfs(0,0);
return res;
}
private void dfs(int cur, int steps){
if(steps == k){
if(cur == n - 1){
res++;
//System.out.println(res);
}
return;
}
List<Integer> l = edges.get(cur);
for(int next : l){
dfs(next, steps + 1);
}
}
}
- 时间复杂度:O(n^k)。
- 空间复杂度:O(n+m+k)。
相应的bfs方法也可以解决本题,在此不再赘述
但两者的时间复杂度显而易见地大,翻评论区却发现了一种更巧妙的方法——动态规划。
思路2:动态规划
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 = 1; i < k; i++){
for(int[] edge : relation){
dp[i][edge[1]] += dp[i - 1][edge[0]];
}
}
return dp[k][n - 1];
}
}
动态规划从代码入手更易于理解。dp数组用于存储 第i轮 中 每个小朋友 可达的方案数,其值为可以向 他 传递信息的小朋友 第i-1轮 可达方案数 的总和。第一层for循环用于遍历轮数,第二层for循环用于遍历边(信息传递的方向)——edge[1]
即为该小朋友,edge[0]
为可以向他传递信息的小朋友,思路理清后,代码也就呼之欲出了。