判断图中是否有负环可用spfa算法,比Bellman-Ford算法效率高。
d[v]=min(d[v],d[u]+w),只有d[u]变小,d[v]才变小,spfa基于这一点进行优化。 只要d[u]变小,则将其压入队列中,并更新以u为起点的所有路径。
例题:spfa求最短路
给定一个n个点m条边的有向图,图中可能存在重边和自环, 边权可能为负数。
请你求出1号点到n号点的最短距离,如果无法从1号点走到n号点,则输出impossible。
数据保证不存在负权回路。
输入格式
第一行包含整数n和m。
接下来m行每行包含三个整数x,y,z,表示存在一条从点x到点y的有向边,边长为z。
输出格式
输出一个整数,表示1号点到n号点的最短距离。
如果路径不存在,则输出”impossible”。
数据范围
1≤n,m≤10^5
图中涉及边长绝对值均不超过10000。
输入样例:
3 3
1 2 5
2 3 -3
1 3 4
输出样例:
2
#include <iostream>
#include <cstring>
#include <queue>
using namespace std;
const int N = 100010, M = 100010, INF = 0x3f3f3f3f;
int dist[N];
bool visited[N];
int head[N], ver[M], edge[M], ne[M];
int idx, n, m;
void add(int x, int y, int z){
ver[idx] = y, edge[idx] = z, ne[idx] = head[x], head[x] = idx ++;
}
int spfa(){
memset(dist, 0x3f, sizeof(dist));
queue<int>Q;
Q.push(1);
dist[1] = 0;
//visited[]存储是否在当前点是否在队列中
visited[1] = true;
while(!Q.empty()){
int x = Q.front();
Q.pop();
visited[x] = false;
for(int i = head[x]; i != -1; i = ne[i]){
int y = ver[i], z = edge[i];
if(dist[y] > dist[x] + z){
dist[y] = dist[x] + z;
if(!visited[y]){
visited[y] = true;
Q.push(y);
}
}
}
}
if(dist[n] == INF) return -1;
return dist[n];
}
int main(){
scanf("%d%d", &n, &m);
while(m --){
int x, y, z;
scanf("%d%d%d", &x, &y, &z);
add(x, y, z);
}
int res = spfa();
if(res == -1) puts("impossible");
else printf("%d", res);
return 0;
}
给定一个n个点m条边的有向图,图中可能存在重边和自环, 边权可能为负数。
请你判断图中是否存在负权回路。
输入格式
第一行包含整数n和m。
接下来m行每行包含三个整数x,y,z,表示点x和点y之间存在一条有向边,边长为z。
输出格式
如果图中存在负权回路,则输出“Yes”,否则输出“No”。
数据范围
1≤n≤2000,1≤m≤10000,图中涉及边长绝对值均不超过10000。
输入样例:
3 3
1 2 -1
2 3 4
3 1 -4
输出样例:
Yes
不需要初始化d数组,判断是否有负环,并不是判断是否只有从1开始的负环,所以要把所有的点都压入队列中。
原理:如果某条最短路径上有n个点(除了自己),那么加上自己之后一共有n+1个点(n条边),由抽屉原理一定有两个点相同,所以存在环,而且一定是负环。
#include <iostream>
#include <queue>
#include <cstring>
using namespace std;
const int N = 2010, M = 10010, INF = 0x3f3f3f3f;
int head[N], ver[M], edge[M], ne[M];
int cnt[N];
/*
cnt[]存储1到x的最短路中经过的点数
visited[]存储每个点是否在队列中
*/
bool visited[N];
int dist[N];
int idx, n, m;
void add(int x, int y, int z){
ver[idx] = y, edge[idx] = z, ne[idx] = head[x], head[x] = idx ++;
}
bool spfa(){
queue<int>Q;
for(int i = 1; i <= n; i ++){
Q.push(i);
visited[i] = true;
}
while(!Q.empty()){
int x = Q.front();
Q.pop();
visited[x] = false;
for(int i = head[x]; i != -1; i = ne[i]){
int y = ver[i], z = edge[i];
if(dist[y] > dist[x] + z){
dist[y] = dist[x] + z;
cnt[y] = cnt[x] + 1;
// 如果从1号点到x的最短路中包含至少n个点(不包括自己),则说明存在环
if(cnt[y] >= n) return true;
if(!visited[y]){
visited[y] = true;
Q.push(y);
}
}
}
}
return false;
}
int main(){
memset(head, -1, sizeof(head));
cin >> n >> m;
while(m --){
int x, y, z;
cin >> x >> y >> z;
add(x, y, z);
}
if(spfa()) cout << "Yes" << endl;
else cout << "No" << endl;
return 0;
}
本文深入讲解了SPFA算法,一种高效的最短路径算法,适用于含有负权边的有向图。文章通过两个实例,详细解释了如何使用SPFA算法求解最短路径问题和判断图中是否存在负权回路。
1643

被折叠的 条评论
为什么被折叠?



