【洛谷-图论】P1807 最长路

题目描述

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 n100 m ≤ 1 0 3 m \leq 10^3 m103
  • 对于 40 % 40\% 40% 的数据, n ≤ 1 0 3 n \leq 10^3 n103 m ≤ 1 0 4 m \leq 10^{4} m104
  • 对于 100 % 100\% 100% 的数据, 1 ≤ n ≤ 1500 1 \leq n \leq 1500 1n1500 1 ≤ m ≤ 5 × 1 0 4 1 \leq m \leq 5 \times 10^4 1m5×104 1 ≤ u , v ≤ n 1 \leq u, v \leq n 1u,vn − 1 0 5 ≤ w ≤ 1 0 5 -10^5 \leq w \leq 10^5 105w105

解题思路

这题可以用拓扑排序,从顶点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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值