LCP 07. 传递信息
小朋友 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]
官方题解
1 深度优先搜索
把关系看成有向图,每个玩家对应一个节点,每个关系对应一条有向边。
本题等价于在有向图中寻找从节点0到节点n-1长度为k的路径数,同一条路径可以重复经过同一个节点。
从节点0出发做深度优先搜索,每一步记录当前所在节点以及经过的轮数。当经过k轮时,如果位于节点n-1,则将方案数加1.搜索结束后,得到总的方案数。
使用列表存储有向边的关系。
class Solution{
public:
int numWays(int n, vector<vector<int>> &relation, int k) {
vector<vector<int>> edges(n);
for (auto &edge : relation) {
int src = edge[0], dst = edge[1];
edges[src].push_back(dst);
}
int ways=0;
function<void(int, int)> dfs = [&] (int index, int steps) {
if (steps == k) {
if (index == n-1) {
++ways;
}
return;
}
for (int to : edges[index]) {
dfs(to, steps+1);
}
};
dfs(0, 0);
return ways;
}
};
2 广度优先搜索
从节点0出发做广度优先搜索,当遍历到k层时,如果位于节点n-1,则将方案数加1.
class Solution{
public:
int numWays(int n, vector<vector<int>> &relation, int k) {
vector<vector<int>> edges(n);
for (auto &edge: relation) {
int src = edge[0], dst=edge[1];
edges[src].push_back(dst);
}
int steps=0;
queue<int> que;
que.push(0);
while (! que.empty() && steps<k) {
steps++;
int size =que.size();
for (int i=-; i<size; i++) {
int index = que.front();
que.pop();
for (auto &nextIndex: edges[index]) {
que.push(nextIndex);
}
}
}
int ways=0;
if (steps ==k) {
while (! que.empty()) {
if (que.front() == n-1) {
ways++;
}
que.pop();
}
}
return ways;
}
}
3 动态规划
定义动态规划的状态dp[i][j]为经过i轮传递到编号j的玩家的方案数,其中0≤i≤k, 0≤j<n
由于从编号0的玩家开始传递,当i=0时,一定位于编号0的玩家,不会传递到其他玩家,因此动态规划的边界情况如下:
d
p
[
0
]
[
j
]
=
{
1
,
j
=
0
0
,
j
≠
0
dp[0][j]=\left\{\begin{array}{l} 1, j=0 \\ 0, j≠0 \end{array}\right.
dp[0][j]={1,j=00,j=0
状态转移方程
d
p
[
i
+
1
]
[
d
s
t
]
=
∑
[
s
r
c
,
d
s
t
]
∈
r
e
l
a
t
i
o
n
d
p
[
i
]
[
s
r
c
]
dp[i+1][dst]=\sum_{[src,dst]∈relation} dp[i][src]
dp[i+1][dst]=[src,dst]∈relation∑dp[i][src]
int numWays(int n, vector<vector<int>>& relatioin, int k) {
vector<vector<int>> dp(k+1, vector<int> (n));
dp[0][0]=1;
for (int i=0; i<k; i++) {
for (auto& edge: relation) {
int src=edge[0], dst=edge[1];
dp[i+1][dst]+=dp[i][src];
}
}
return dp[k][n-1];
}
空间复杂度O(kn)。i大于0时,dp[i][]的值只和dp[i-1][]的值有关,因此将二维数组变成一维数组,空间复杂度O(n)。
int numWays(int n, vector<vector<int>>& relation, int k) {
vector<int> dp(n);
dp[0]=1;
for (int i=0; i<k; i++) {
vector<int> next(n);
for (auto& edge : relation) {
int src=edge[0], dst=edge[1];
next[dst]+=dp[src];
}
dp=next;
}
return dp[n-1];
}