问题描述
给定一个n个顶点,m条边的有向图(其中某些边权可能为负,但保证没有负环)。请你计算从1号点到其他点的最短路(顶点从1到n编号)。
输入格式
第一行两个整数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 -1
2 3 -1
3 1 2
样例输出
-1
-2
-2
数据规模与约定
对于10%的数据,n = 2,m = 2。
对于30%的数据,n <= 5,m <= 10。
对于100%的数据,1 <= n <= 20000,1 <= m <= 200000,-10000 <= l <= 10000,保证从任意顶点都能到达其他所有顶点。
哈哈,又是这道题,这次使用更加简易的Bellman-Ford算法,思路也简单,
就是不断地进行松弛操作,而在一个N个的图中,任意两个点之间的最短路径
最多可以包含n-1条边,也就是说,每条边的松弛操作最多要进行n-1次,
对,每条边,也就是说时间复杂度为O(mn),看起来比Dijkstra算法还要不靠谱,
但是,由于大部分情况我们不必进行N-1次循环就可以得到最短路,所以我们可以
用个flag标记,如果在一轮松弛操作中没有可以松弛的,说明所有的路径都是最短路,这时候就可以退出循环。
下面是 只有4行代码 的 Bellman-Ford算法,
只有4行,简直是是我这样记忆力差的人福音啊
for(int i=1;i<=n-1;i++) //外层 循环 n-1次
for(int j=1;j<=m;j++) //每条边
if(dis[v[j]] > dis[u[j]] + w[j]){ //松弛操作,如果1号顶点到v[j]的距离大于 u[j]的距离加上 u[j]->v[j] 这条边的距离
dis[v[j]] = dis[u[j]] + w[j];
}
#include<iostream>
#define MAX_NUM 200010
using namespace std;
int dis[MAX_NUM];
int u[MAX_NUM];
int v[MAX_NUM];
int w[MAX_NUM];
int n,m;
const int inf =9999999;
int main()
{
freopen("2.txt","r",stdin);
freopen("22.txt","w+",stdout);
cin>>n>>m;
//初始化
for(int i=1;i<=n;i++)
dis[i]=inf;
for(int i=1;i<=m;i++)
{
cin>>u[i]>>v[i]>>w[i];
}
dis[1]=0;
//bellman 算法核心
for(int i=1;i<=n-1;i++){
int flag=0; //标记
for(int j=1;j<=m;j++){
if(dis[v[j]] > dis[u[j]] + w[j]){
dis[v[j]] = dis[u[j]] + w[j];
flag=1;
}
}
if(flag==0) break;
//如果这一轮没有一条边松弛,说明所有的路径都是最短的,退出循环
}
for(int i=2;i<=n;i++)
printf("%d\n",dis[i]);
return 0;
}
//更新,下面是Bellman算法的队列优化,将经过松弛操作的顶点入队,并将队列中的顶点全部进行松弛操作,如果队列中已经存在此顶点便不重复入队,用book数组来标记。这样可以避免无谓的对一些顶点进行松弛操作,达到优化的效果。
使用邻接表的形式存储边。。。
#include<iostream>
#define MAX_NUM 200010
#define MAX 20010
using namespace std;
int dis[MAX_NUM]; //1号顶点到个顶点的距离
int u[MAX_NUM]; //左顶点
int v[MAX_NUM]; //右顶点
int w[MAX_NUM]; //权重
int first[MAX_NUM]; //邻接表
int next[MAX_NUM];
int que[MAX_NUM]; //队列
int tail=1,head=1; //队首 队尾
int book[MAX_NUM]; //标记数组
int n,m; //顶点数 边数
const int inf = 999999;
int main()
{
freopen("2.txt","r",stdin);
cin>>n>>m;
//初始化
for(int i=1;i<=n;i++)
first[i]=-1;
for(int i=1;i<=n;i++)
dis[i] = inf;
dis[1]=0; //1号顶点到自身的距离
for(int i=1;i<=m;i++)
{
cin>>u[i]>>v[i]>>w[i];
next[i] = first[u[i]]; //first[u[i]]保存 u[i]顶点 的第一条边
first[u[i]] = i; //next[i] 保存编号为 i 的 下一条边
//邻接表的边是插入表头而不是表尾
}
que[tail++]=1; //1号顶点入队
book[1]=1;
while(head<tail){ //队不为空
int k=first[que[head]]; //取队头元素
while(k!=-1){
if(dis[v[k]] > dis[u[k]] + w[k]){ //松弛
dis[v[k]] = dis[u[k]] + w[k];
if(book[v[k]]==0){ //如果顶点不在队列中
que[tail]=v[k]; //入队并标记
tail++;
book[v[k]]=1;
}
}
k=next[k]; //下一条边
}
book[que[head]]=0; //出队并取消标记
head++;
}
for(int i=2;i<=n;i++)
cout<<dis[i]<<endl;
return 0;
}