算法训练 最短路
锦囊1
使用最短路算法。
问题描述
给定一个n个顶点,m条边的有向图(其中某些边权可能为负,但保证没有负环)。请你计算从1号点到其他点的最短路(顶点从1到n编号)。
分析:具有负值的单源最短路径使用bellman-ford(贝尔曼-福特)算法。
方法一:带判断的bellman-ford
bellman-ford算法逐个添加最短路径,最短路径最多有n-1条,故该算法最多进行n-1次。有时会小于n-1次,如果不在能优化,则已经是最优的了
#include <iostream>
using namespace std;
const int inf=1e6;
int u[200001],v[200000],w[200000];
int dis[20000];
int main(int argc, char** argv) {
int n,m;
cin>>n>>m;
for(int i=2;i<=n;i++)
dis[i]=inf;
//到自身初始化为0,便于松弛。
dis[1]=0;
for(int i=0;i<m;i++)
cin>>u[i]>>v[i]>>w[i];
for(int i=1;i<n;i++){
bool flag=false;
for(int j=0;j<m;j++){
if(dis[v[j]]>dis[u[j]]+w[j]){
flag=true;
dis[v[j]]=dis[u[j]]+w[j];
}
}
//已经是最小路径,不需要继续优化
if(!flag)
break;
}
for(int i=2;i<=n;i++)
cout<<dis[i]<<endl;
return 0;
}
方法二:SPFA,队列优化的bellman-ford
#include <iostream>
#include <queue>
#include <vector>
using namespace std;
struct E{
int next,w;
};
const int inf=1e8;
queue<int> q;
int dis[20001];
bool use[20001];
vector<E> e[20001];
int main(int argc, char** argv) {
int n,m;
cin>>n>>m;
E edge;
for(int i=0;i<m;i++){
int u;
cin>>u>>edge.next>>edge.w;
e[u].push_back(edge);
}
for(int i=2;i<=n;i++)
dis[i]=inf;
dis[1]=0;
q.push(1);
while(!q.empty()){
int k=q.front();
q.pop();
for(int i=0;i<e[k].size();i++){
int next=e[k][i].next;
int w=e[k][i].w;
if(dis[next]>dis[k]+w){
dis[next]=dis[k]+w;
//成功松弛且不在队列中的顶点入队
if(!use[next]){
q.push(next);
use[next]=true;
}
}
}
//出队并标记没被使用。
use[k]=false;
}
for(int i=2;i<=n;i++)
cout<<dis[i]<<endl;
return 0;
}
输入格式
第一行两个整数n, m。
接下来的m行,每行有三个整数u, v, l,表示u到v有一条长度为l的边。
输出格式
共n-1行,第i行表示1号点到i+1号点的最短路。
样例输入
3 3
1 2 -1
2 3 -1
3 1 2
样例输出
-1
-2
数据规模与约定
对于10%的数据,n = 2,m = 2。
对于30%的数据,n <= 5,m <= 10。
对于100%的数据,1 <= n <= 20000,1 <= m <= 200000,-10000 <= l <= 10000,保证从任意顶点都能到达其他所有顶点。