题目链接:
1786. 从第一个节点出发到最后一个节点的受限路径数 - 力扣(LeetCode) (leetcode-cn.com)
题意:
给你由n个点构成的m条边(x, y, w){从x到y有一条无向边,长度为w},构成一个连通图,然后让每个点到点n的最短路径距离作为当前点的权值,然后问从点1到点n有几条不同的路径(每个点必须往比自己权值小的点走)。
- 1 <= n <= 2 * 104
- n - 1 <= edges.length <= 4 * 104
- edges[i].length == 3
- 1 <= ui, vi <= n
- ui != vi
- 1 <= weighti <= 105
- 任意两个节点之间至多存在一条边
- 任意两个节点之间至少存在一条路径
最后答案对1e9+7取模
题解:
Step.1 :通过求单源最短路径确定每个点的权值。
Step.2 :拓扑排序,在排序过程中有几个点需要注意:
1. 计算入度的时候,遍历整个图,如果 x 与 y 之间有路,且,dis[x] > dis[y] , 那么 du[y] ++;, 因为我们要从权值高的点往权值低的点走。
2. 不论点1的度为多少,都要将其放在拓扑排序队列中的队首,因为路径是从点1开始的。
3. 需要初始化在点1的位置的路径个数为 1,这样才能往后递推,如果点1可以通往点2,那么 ans[2] = (ans[2] + ans[1])%mod ; 推广至点x可以通往点y,那么 ans[x] = (ans[x] + ans[y]) % mod;
4. 为什么需要拓扑排序往后地推,直接在点1 遍历往后推不可以吗?当然是不可以的,这是因为在推的过程中 如果点 x 通向 点z ,这时候 ans[z] = (ans[x] + ans[z]) % mod , 但是之后还可能会有点通向点x,这时候ans[x] 变化了,但是已经给 ans[z] 赋值了,所以最终答案是不确定的,可能多也可能少路径的个数,但一定是不对的。拓扑排序保证了点都在有序的推进,不会出现“抢跑”的情况。
AC代码:
class Solution{
public:
typedef pair<int, int> P;
static const int maxn = 20007;
const int INF = 0x3f3f3f3f;
const int mod = 1e9 + 7;
int dis[maxn], vis[maxn], du[maxn], dp[maxn]; // 这里的dp数组相当于题解中的 ans 数组
vector<P> G[maxn];
void spfa(int n){ // 求单源最短路径
for(int i = 0; i <= n; i++) dis[i] = INF, vis[i] = 0;
queue<int> que;
que.push(n);
dis[n] = 0;
vis[n] = 1;
while(!que.empty())
{
int curv = que.front(); que.pop();
vis[curv] = 0;
for(int i = 0; i < G[curv].size(); i++)
{
P t = G[curv][i];
if(dis[curv] + t.second < dis[t.first])
{
dis[t.first] = dis[curv] + t.second;
if(!vis[t.first]) que.push(t.first);
}
}
}
}
void topo(int n) // 拓扑排序
{
memset(du, 0, sizeof(du));
for(int i = 1; i <= n; i++)
{
for(int j = 0; j < G[i].size(); j++)
{
int nxt = G[i][j].first;
if(dis[i] > dis[nxt]) du[nxt]++; // 求入度
}
}
du[1] = 0;
queue<int> que;
que.push(1); // 保证点1在队首
memset(dp, 0, sizeof(dp));
dp[1] = 1;
for(int i = 2; i <= n; i++) if(du[i] == 0) que.push(i); // 这些点也都要,不然可能拓扑排序会中止,有些地方“解不开”
while(!que.empty())
{
int curv = que.front(); que.pop();
for(int i = 0; i < G[curv].size(); i++)
{
int nxt = G[curv][i].first;
if(dis[curv] > dis[nxt])
{
du[nxt] --;
dp[nxt] = (dp[nxt] + dp[curv]) % mod; // 递推
if(!du[nxt]) que.push(nxt);
}
}
}
}
int countRestrictedPaths(int n, vector<vector<int>>& edge){
for(int i = 0; i < edge.size(); i++)
{
G[ edge[i][0] ].push_back( P(edge[i][1], edge[i][2]) );
G[ edge[i][1] ].push_back( P(edge[i][0], edge[i][2]) ); // 无向图
}
spfa(n);
topo(n);
return dp[n];
}
};