题目描述
设 G G G 为有 n n n 个顶点的带权有向无环图, G G G 中各顶点的编号为 1 1 1 到 n n n,请设计算法,计算图 G G G 中 < 1 , n > \lt1,n\gt <1,n> 间的最 长路径。
输入输出格式
输入格式
输入的第一行有两个整数,分别代表图的点数
n
n
n 和边数
m
m
m。
第
2
2
2 到第
(
m
+
1
)
(m + 1)
(m+1) 行,每行
3
3
3 个整数
u
,
v
,
w
u, v, w
u,v,w,代表存在一条从
u
u
u 到
v
v
v 边权为
w
w
w 的边。
输出格式
输出一行一个整数,代表
1
1
1 到
n
n
n 的最长路。
若
1
1
1 与
n
n
n 不联通,请输出
−
1
-1
−1。
输入输出样例
输入样例 #1
2 1
1 2 1
输出样例 #1
1
说明
数据规模与约定
- 对于 20 % 20\% 20%的数据, n ≤ 100 n \leq 100 n≤100, m ≤ 1 0 3 m \leq 10^3 m≤103。
- 对于 40 % 40\% 40% 的数据, n ≤ 1 0 3 n \leq 10^3 n≤103, m ≤ 1 0 4 m \leq 10^{4} m≤104。
- 对于 100 % 100\% 100% 的数据, 1 ≤ n ≤ 1500 1 \leq n \leq 1500 1≤n≤1500, 1 ≤ m ≤ 5 × 1 0 4 1 \leq m \leq 5 \times 10^4 1≤m≤5×104, 1 ≤ u , v ≤ n 1 \leq u, v \leq n 1≤u,v≤n, − 1 0 5 ≤ w ≤ 1 0 5 -10^5 \leq w \leq 10^5 −105≤w≤105。
解题思路
这题可以用拓扑排序,从顶点1出发,依次记录刷新到i点的最长路径存入f[i]中,最后输出f[n]即可。
但需要注意,题目中并没有说只有1为入度为0的顶点,也就是说还有可能存在其它入度为0的顶点,如果把这些点也加入计算,可能导致错误结果,因为求的是从1到n的最长路径不是i到n。而如果不管这些顶点,又会影响拓扑过程,因为只有当一个顶点入度为0时才会加入队列,而如果一个入度为0的顶点
i
i
i 指向另一个顶点
x
x
x ,因为顶点
i
i
i 在拓扑过程中无法到达,所以顶点
x
x
x 就永远入度大于0,导致无法进入队列,相当于封死了这条路。
所以在拓扑前还需要把这些除1外的入度为0的顶点去除。
//去除掉除了n以外的入度为0的点
bool seenInd0[MAXN] = { false }; //已经去除的入度为0的顶点标记为true
for (int idx = 2; idx <= n; idx++) {
if (ind[idx] == 0&&seenInd0[idx]==false) {
for (int j = 0; j < edges[idx].size(); j++) {
int toP = edges[idx][j].first; //到达的点
ind[toP]--;
}
seenInd0[idx] = true;
idx = 1; //重新搜索,因为可能产生新的入度为0的点
}
}
完整代码
#include<iostream>
#include<algorithm>
#include<vector>
#include<queue>
using namespace std;
const int MAXN = 1505;
vector<pair<int,int> > edges[MAXN]; //存点idx能到达点first,权值为second
int ind[MAXN],f[MAXN]; //入度,到当前点的最大权值和
queue<int> que;
int main() {
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
int n, m,tmp1,tmp2,tmp3;
bool find = false; //能到达n
cin >> n >> m;
for (int i = 1; i <= m; i++) {
cin >> tmp1 >> tmp2 >> tmp3;
edges[tmp1].push_back(make_pair(tmp2, tmp3));
ind[tmp2]++;
}
//去除掉除了n以外的入度为0的点
bool seenInd0[MAXN] = { false };
for (int idx = 2; idx <= n; idx++) {
if (ind[idx] == 0&&seenInd0[idx]==false) {
for (int j = 0; j < edges[idx].size(); j++) {
int toP = edges[idx][j].first; //到达的点
ind[toP]--;
}
seenInd0[idx] = true;
idx = 1; //重新搜索,因为可能产生新的入度为0的点
}
}
que.push(1);
while (que.empty() == false) {
int u = que.front();
que.pop();
for (int i = 0; i < edges[u].size(); i++) {
int v = edges[u][i].first,power = edges[u][i].second;
f[v] = max(f[v], f[u]+power);
ind[v]--;
if(ind[v]==0)que.push(v);
if (v == n)find = true;
}
}
cout << (find? f[n]:-1);
return 0;
}