何时用:Dijkstra算法,Bellman Ford算法,SPFA算法,Floyd算法
Floyd(弗洛伊德)算法:
适用范围:
可求多源最短路
无负权回路即可(负权回路:a点到a点的权值和为负),边权可正可负,运行一次算法即可求得任意两点间最短路,可以判(负)环。
运用领接矩阵,求解数据量较小问题
dis作为邻接矩阵
dis[i][j] k 表示:i结点到j结点只经过前k号点的最短路程
for(int k=1;k<=n;k++)
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
dis[i][j]=min(dis[i][j],dis[i][k]+dis[k][j])
其他应用:
传递闭包:
对于一个结点k,如果j能到k,i能到k,那么i就能到j,求传递闭包,就是把图中所有满足这样传递性的结点计算出来。
O(n^3)
for(int k=1;k<=n;k++)
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
if(dis[i][k]&&dis[j][k])
dis[i][j]=1;
传递闭包重要优化bitset O(n^3/64)
时间复杂度:
用到较少
求最小环
无向图核心算法:
int map[][]=new int[n+1][n+1];//邻接矩阵
int dis[][]=new int[n+1][n+1];//dis[i][j]为i到j的最短路
for(int k=1;k<=n;k++)//k为中间结点 从1到n
{
//第一部分:枚举经过i,j,k的最小环,此时i和j都要小于k,避免路线重复,之后才能更新k点
for(int i=1;i<k;i++)
{
for(int j=i+1;j<k;j++)
{
if(map[i][k]==Integer.MAX_VALUE||map[k][j]==Integer.MAX_VALUE) continue;
minload=Math.min(minload,dis[i][j]+map[i][k]+map[k][j]);
}
}
//第二部分:更新k点,往后更新最短路
for(int i=1;i<=n;i++)
{
for(int j=1;j<=n;j++)
{
dis[i][j]=Math.min(dis[i][j],dis[i][k]+dis[k][j]);
}
}
}
有向图核心算法:
dp[i][j] k 表示:i结点到j结点只经过前k号点的最短路程
for(int k=1;k<=n;k++)
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
dis[i][j]=min(dis[i][j],dis[i][k]+dis[k][j])
int minload=Integer.MAX_VALUE;
for(int i=1;i<=n;i++)
minload=Math.min(minload,dis[i][i]);
HDU 1599
为无向图题目
package shortestpath;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
//import java.io.InputstreamReader;
//2021年3月23日下午1:35:52
//writer:apple
public class floydhdu1599 {
static int n;
static int m;
public static void main(String[] args) throws IOException {
// TODO Auto-generated method stub
BufferedReader br=new BufferedReader(new InputStreamReader(System.in));
while(true)
{
String t[]=br.readLine().split(" ");
n=Integer.parseInt(t[0]);m=Integer.parseInt(t[1]);
int minload=Integer.MAX_VALUE;
int map[][]=new int[n+1][n+1];//邻接矩阵
int dis[][]=new int[n+1][n+1];//dis[i][j]为i到j的最短路
for(int i=0;i<=n;i++)
{
for(int j=0;j<=n;j++)
{
map[i][j]=Integer.MAX_VALUE;
}
}
for(int i=0;i<m;i++)
{
String tt[]=br.readLine().split(" ");
int a=Integer.parseInt(tt[0]);int b=Integer.parseInt(tt[1]);int weignt=Integer.parseInt(tt[2]);
map[a][b]=weignt;
map[b][a]=weignt;
dis[a][b]=weignt;
dis[b][a]=weignt;
}
for(int k=1;k<=n;k++)//k为中间结点 从1到n
{
//第一部分:枚举经过i,j,k的最小环,此时i和j都要小于k,避免路线重复,之后才能更新k点
for(int i=1;i<k;i++)
{
for(int j=i+1;j<k;j++)
{
if(map[i][k]==Integer.MAX_VALUE||map[k][j]==Integer.MAX_VALUE) continue;
minload=Math.min(minload,dis[i][j]+map[i][k]+map[k][j]);
}
}
//第二部分:更新k点,往后更新最短路
for(int i=1;i<=n;i++)
{
for(int j=1;j<=n;j++)
{
dis[i][j]=Math.min(dis[i][j],dis[i][k]+dis[k][j]);
}
}
}
if(minload==Integer.MAX_VALUE) System.out.println("It's impossible.");
else System.out.println(minload);
}
}
}
Dijkstra(迪杰斯特拉) 算法
适用范围:
只适用于不含负权边的图,本质为贪心。
为单源最短路径算法,只能求一个点到其他点的最短路径,并不像Floyd算法可以求任意两点的最短路。
时间复杂度为:
上限为O(N^2)
遍历n个点找最小值,为O(n),考虑用优先队列,直接取队首元素,就能降低为O(logn)
代码实现的:
邻接链表建图
优先队列
重载运算符
快读
经典:
public static void Dijkstra()
{
p.offer(new Edge(s,0));//起点 权值为0
while(!p.isEmpty())
{
Edge t=p.poll();
int from=t.to;
int fromw=t.weight;
if(vis[from]==1) continue;
vis[from]=1;//只有从优先队列里弹出后才能确认是最短的,才能更新vis和dis
for(int i=0;i<map[from].size();i++)
{
int next=map[from].get(i).to;
int nextw=map[from].get(i).weight;
if(dis[from]+nextw<dis[next])//起点经过from点到next的最小值 和 起点到next的最小值作比较
{
dis[next]=dis[from]+nextw;
// if(vis[next]==0)
p.offer(new Edge(next,nextw));
}
}
}
}
优化时间后的:
public static void Dijkstra()
{
PriorityQueue<Edge> p=new PriorityQueue<Edge>((a,b)->a.weigth-b.weigth);
p.add(new Edge(s,0));
while(!p.isEmpty())
{
Edge t=p.poll();
int from=t.to;
int fromw=t.weigth;
if(vis[from]) continue;
vis[from]=true;
dis[from]=fromw;
for(Edge temp:map[from])
{
int next=temp.to;
int nextw=temp.weigth;
p.offer(new Edge(next,fromw+nextw));
}
}
}
例题:
struct node{
int to,w;
node(int tt,int ww):to(tt),w(ww){}
bool operator<(const node &cmp)const{
return w>cmp.w;
}
};
#include<bits/stdc++.h>
#include<iostream>
using namespace std;
const double eps = 1e-8; //const只是用来定义常量,常量在代码中一直存在,但define是预处理功能,有本质区别
const int maxx = 0x7f7f7f7f;//0x7f7f7f7f表示数据的无穷大
//常用的浮点数比较宏定义:
#define Equ(a,b) ((fabs((a)-(b)))<(eps)) //等于
#define More(a,b) (((a)-(b))>(esp)) //大于
#define Less(a,b) (((a)-(b))<(-esp))//小于
#define MoreEqu(a,b) (((a)-(b))>(-esp))//大于等于
#define LessEqu(a,b) (((a)-(b))<(esp))//小于等于
#define MAX( x, y ) ( ((x) > (y)) ? (x) : (y) )//
//使用了algorithm头文件就可以直接使用max函数;
#define MIN( x, y ) ( ((x) < (y)) ? (x) : (y) )
#define ll long long
#define PI 3.1415926
#define eps 1e-8
#define Conn(x,y) x##y;
int n;
int end;
struct node{
int to,w;
node(int tt,int ww):to(tt),w(ww){}
bool operator<(const node &cmp)const{
return w>cmp.w;
}
};
vector<node> ma[2000];
bool vis[2000];
int dis[2000];
void dij(int x)
{
priority_queue<node> p;
p.push(node(x,0));
while(!p.empty())
{
node t=p.top();p.pop();
int from=t.to;
int fromw=t.w;
if(vis[from]) continue;
vis[from]=true;
dis[from]=fromw;
for(int i=0;i<ma[from].size();i++)
{
int next=ma[from][i].to;
int nextw=ma[from][i].w;
p.push(node(next,fromw+nextw));
}
}
}
int main()
{
cin>>n>>end;
for(int i=1;i<=n;i++)
{
int x,y,l;
cin>>x>>y>>l;
ma[x].push_back(node(y,l));
ma[y].push_back(node(x,l));
}
dij(1);
cout<<dis[end];
return 0;
}
未解决MLE和TLE的代码
package shortestpath;
//import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.io.StreamTokenizer;
import java.util.LinkedList;
import java.util.PriorityQueue;
//2021年3月23日下午8:09:41
//writer:apple
public class DijkstraP4779 {
static int n;
static int m;
static int s;
static int dis[];//dis[i]为s到i的最短距离
static int vis[];
static class Edge{
int to;
int weight;
public Edge(int t,int w) {
to=t; weight=w;
}
}
static LinkedList<Edge> map[];
static PriorityQueue<Edge> p=new PriorityQueue<>((a,b)->a.weight-b.weight);
public static void main(String[] args) throws IOException {
// TODO Auto-generated method stub
// BufferedReader br=new BufferedReader(new InputStreamReader(System.in));
StreamTokenizer st=new StreamTokenizer(new InputStreamReader(System.in));
// String t[]=br.readLine().split(" ");
st.nextToken();n=(int) st.nval;
st.nextToken();m=(int)st.nval;
st.nextToken();s=(int)st.nval;
map=new LinkedList[n+1];
dis=new int[n+1];
vis=new int[n+1];
for(int i=0;i<n+1;i++)
{
dis[i]=Integer.MAX_VALUE;
map[i]=new LinkedList<Edge>();//讨论
}
dis[s]=0;
for(int i=0;i<m;i++)
{
// int from=Integer.parseInt(tt[0]);int to=Integer.parseInt(tt[1]);int w=Integer.parseInt(tt[2]);
st.nextToken();int from=(int)st.nval;
st.nextToken();int to=(int)st.nval;
st.nextToken();int w=(int)st.nval;
map[from].add(new Edge(to,w));
}
Dijkstra();
PrintWriter pt=new PrintWriter(new OutputStreamWriter(System.out));
String ans="";
for(int i=1;i<=n;i++)
{
ans+=dis[i]+" ";
}
pt.println(ans);
pt.flush();
// StringBuilder ans=new StringBuilder();
// for(int i=1;i<=n;i++)
// {
// ans.append(dis[i]+" ");
// }
// System.out.println(ans);
}
public static void Dijkstra()
{
p.offer(new Edge(s,0));//起点 权值为0
while(!p.isEmpty())
{
Edge t=p.poll();
int from=t.to;
int fromw=t.weight;
if(vis[from]==1) continue;
vis[from]=1;//只有从优先队列里弹出后才能确认是最短的,才能更新vis和dis
for(int i=0;i<map[from].size();i++)
{
int next=map[from].get(i).to;
int nextw=map[from].get(i).weight;
if(dis[from]+nextw<dis[next])//起点经过from点到next的最小值 和 起点到next的最小值作比较
{
dis[next]=dis[from]+nextw;
// if(vis[next]==0)
p.offer(new Edge(next,nextw));
}
}
}
}
}
经过多种优化后:
1,static全覆盖
2,优化输入:
StreamTokenizer st=new StreamTokenizer(new BufferedReader(new InputStreamReader(System.in)));
3,优化输出:
PrintWriter pr=new PrintWriter(new OutputStreamWriter(System.out));
for(int i=1;i<=n;i++)
{
ans.append(dis[i]);ans.append(" ");
}
pr.println(ans);
pr.flush();
4,优化算法:
p.add(new Edge(s,0));
while(!p.isEmpty())
{
Edge t=p.poll();
int from=t.to;
int fromw=t.weigth;
if(vis[from]) continue;
vis[from]=true;
dis[from]=fromw;
for(Edge temp:map[from])
{
int next=temp.to;
int nextw=temp.weigth;
p.offer(new Edge(next,fromw+nextw));
}
}
AC代码:
package shortestpath;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.io.StreamTokenizer;
import java.util.ArrayList;
import java.util.PriorityQueue;
//2021年3月24日上午9:50:14
//writer:apple
public class Dijkstra最终版 {
static int n;
static int m;
static int s;
static class Edge{
int to;
int weigth;
public Edge(int t,int w)
{
to=t;
weigth=w;
}
}
static PriorityQueue<Edge> p=new PriorityQueue<Edge>((a,b)->a.weigth-b.weigth);
static ArrayList<Edge> map[];
static boolean vis[];
static int dis[];
public static void main(String[] args) throws IOException {
// TODO Auto-generated method stub
StreamTokenizer st=new StreamTokenizer(new BufferedReader(new InputStreamReader(System.in)));
st.nextToken();n=(int)st.nval;
st.nextToken();m=(int)st.nval;
st.nextToken();s=(int)st.nval;
//变量初始化
map=new ArrayList[n+1];
vis=new boolean[n+1];
dis=new int[n+1];
for(int i=0;i<n+1;i++)
{
map[i]=new ArrayList<Edge>();
dis[i]=Integer.MAX_VALUE;
vis[i]=false;
}
dis[1]=0;
for(int i=0;i<m;i++)
{
st.nextToken();int f=(int)st.nval;
st.nextToken();int t=(int)st.nval;
st.nextToken();int w=(int)st.nval;
map[f].add(new Edge(t,w));
}
Dijkstra();
StringBuilder ans=new StringBuilder();
PrintWriter pr=new PrintWriter(new OutputStreamWriter(System.out));
for(int i=1;i<=n;i++)
{
ans.append(dis[i]);ans.append(" ");
}
pr.println(ans);
pr.flush();
}
public static void Dijkstra()
{
p.add(new Edge(s,0));
while(!p.isEmpty())
{
Edge t=p.poll();
int from=t.to;
int fromw=t.weigth;
if(vis[from]) continue;
vis[from]=true;
dis[from]=fromw;
for(Edge temp:map[from])
{
int next=temp.to;
int nextw=temp.weigth;
p.offer(new Edge(next,fromw+nextw));
}
}
}
}
#include<bits/stdc++.h>
#include<iostream>
using namespace std;
const double eps = 1e-8; //const只是用来定义常量,常量在代码中一直存在,但define是预处理功能,有本质区别
const int maxx = 0x7f7f7f7f;//0x7f7f7f7f表示数据的无穷大
//常用的浮点数比较宏定义:
#define Equ(a,b) ((fabs((a)-(b)))<(eps)) //等于
#define More(a,b) (((a)-(b))>(esp)) //大于
#define Less(a,b) (((a)-(b))<(-esp))//小于
#define MoreEqu(a,b) (((a)-(b))>(-esp))//大于等于
#define LessEqu(a,b) (((a)-(b))<(esp))//小于等于
#define MAX( x, y ) ( ((x) > (y)) ? (x) : (y) )//
//使用了algorithm头文件就可以直接使用max函数;
#define MIN( x, y ) ( ((x) < (y)) ? (x) : (y) )
#define ll long long
#define PI 3.1415926
#define eps 1e-8
#define Conn(x,y) x##y;
int n,m;
int sta;
int dis[100005];
bool vis[100005];
struct node
{
int to,w;
node(int tt,int ww):to(tt),w(ww){
}
bool operator <(const node &cmp)const
{
return w>cmp.w;
}
};
vector<node> mapp[100005];
void dijkstra(int x)
{
priority_queue<node> q;
q.push(node(x,0));
while(!q.empty())
{
node now=q.top();q.pop();
int from=now.to;
int fromw=now.w;
if(vis[from])continue;
vis[from]=true;
dis[from]=fromw;
for(int i=0;i<mapp[from].size();i++)
{
int next=mapp[from][i].to;
int nextw=mapp[from][i].w;
q.push(node(next,fromw+nextw));
}
}
}
int main()
{
cin>>n>>m>>sta;
for(int i=1;i<=m;i++)
{
int x,y,l;
cin>>x>>y>>l;
mapp[x].push_back(node(y,l));
mapp[y].push_back(node(x,l));
}
dijkstra(sta);
cout<<dis[1];
for(int i=2;i<=n;i++)
{
cout<<" "<<dis[i];
}
return 0;
}
L3-005 垃圾箱分布
#include<bits/stdc++.h>
#include<iostream>
using namespace std;
const double eps = 1e-8; //const只是用来定义常量,常量在代码中一直存在,但define是预处理功能,有本质区别
const int maxx = 0x7f7f7f7f;//0x7f7f7f7f表示数据的无穷大
//常用的浮点数比较宏定义:
#define Equ(a,b) ((fabs((a)-(b)))<(eps)) //等于
#define More(a,b) (((a)-(b))>(esp)) //大于
#define Less(a,b) (((a)-(b))<(-esp))//小于
#define MoreEqu(a,b) (((a)-(b))>(-esp))//大于等于
#define LessEqu(a,b) (((a)-(b))<(esp))//小于等于
#define MAX( x, y ) ( ((x) > (y)) ? (x) : (y) )//
//使用了algorithm头文件就可以直接使用max函数;
#define MIN( x, y ) ( ((x) < (y)) ? (x) : (y) )
#define ll long long
#define PI 3.1415926
#define eps 1e-8
#define Conn(x,y) x##y;
const int maxn=1e4+10;
int n,m,k,mmax;
struct node
{
int v,w;
node(int vv,int yy)
{
this->v=vv;
this->w=yy;
}
};
bool operator<(node a,node b)
{
return a.w>b.w;//升序,弹出最短的权值
}
vector<node> a[maxn];
int vis[maxn],d[maxn];
int get_num(string x)
{
int l=x.size();
int p=0,sum=0;
if(x[0]=='G') p=1;
for(int i=p;i<l;i++)
{
sum=sum*10+x[i]-'0';
}
if(p) return sum+n;
return sum;
}
void dijkstra(int s)
{
fill(vis,vis+maxn,0);
priority_queue<node> q;
fill(d+1,d+n+m+1,maxx);
d[s]=0;
q.push(node(s,0));
while(!q.empty())
{
node now=q.top();q.pop();
int from=now.v;
int fromw=now.w;
if(vis[from]) continue;
vis[from]=1;
d[from]=fromw;
for(int i=0;i<a[from].size();i++)
{
int next=a[from][i].v;
int nextw=a[from][i].w;
q.push(node(next,fromw+nextw));
}
}
}
int main()
{
int z,nx,ny;
string x,y;
cin>>n>>m>>k>>mmax;
getchar();
for(int i=0;i<k;i++)
{
cin>>x>>y>>z;
nx=get_num(x);
ny=get_num(y);
a[nx].push_back(node(ny,z));
a[ny].push_back(node(nx,z));
}
bool sign=false;
int no=0,ans=0,mmin=0;
for(int i=n+1;i<=n+m;i++)//遍历每一个桶
{
int j=1,sum=0,mind=maxx;
dijkstra(i);
for(;j<=n;j++)
{
if(d[j]>mmax) break;
sum+=d[j];
if(d[j]<mind) mind=d[j];
}
if(j>n)//表示有ans
{
sign=true;
if(mind>mmin)
{
ans=sum;
mmin=mind;
no=i;
}
else if(mind==mmin && ans>sum)
{
ans=sum;no=i;
}
}
}
if(!sign) printf("No Solution\n");
else printf("G%d\n%.1f %.1f\n",no - n,mmin * 1.0,ans * 1.0 / n);
return 0;
}
SPFA算法
适用范围:
求解单源最短路径
可以处理正边权,负边权的最短路,可以处理负环,可以判环。
算法平均复杂度为O(KM),有很高效率。但是出题人往往会卡极端数据,所以对于普通最短路问题,一定要慎用spfa
它是一个万能的算法,它不仅可以求最短路,而且负权边,负环,判环问题都能解决。
一般在Dijkstra解决不了的时候,用SPFA,SPFA为Bellman的优化算法。
思想:运用动态逼近法,通过一个点中转来缩短距离。只有一个点在上一轮中被松弛成功时,这一轮从这个点连出的点才有可能被成功松弛。(松弛:发现更短的路径)
1,Bellman_ford算法里最后return-1的判断条件写的是dist[n]>0x3f3f3f3f/2;而spfa算法写的是dist[n]==0x3f3f3f3f;其原因在于Bellman_ford算法会遍历所有的边,因此不管是不是和源点连通的边它都会得到更新;但是SPFA算法不一样,它相当于采用了BFS,因此遍历到的结点都是与源点连通的,因此如果你要求的n和源点不连通,它不会得到更新,还是保持的0x3f3f3f3f。
,2,Bellman_ford算法可以存在负权回路,是因为其循环的次数是有限制的因此最终不会发生死循环;但是SPFA算法不可以,由于用了队列来存储,只要发生了更新就会不断的入队,因此假如有负权回路请你不要用SPFA否则会死循环。
3, 由于SPFA算法是由Bellman_ford算法优化而来,在最坏的情况下时间复杂度和它一样即时间复杂度为 O(nm)O(nm) ,假如题目时间允许可以直接用SPFA算法去解Dijkstra算法的题目。(好像SPFA有点小小万能的感觉?)
4, 求负环一般使用SPFA算法,方法是用一个cnt数组记录每个点到源点的边数,一个点被更新一次就+1,一旦有点的边数达到了n那就证明存在了负环。
Dijkstra和SPFA区别:
Dijkstra:每个结点一旦出队说明已经为最短路径,vis[i]=true,表示访问过了,需要优先队列,只能处理正边权
SPFA:每个结点可能多次入队,vis[i]=true表示在队列内,出了队列要标记false,只要普通队列,可以处理负边权
应用:
求单源最短路径
可判负环
n个点中,cnt记录到某个点最短路经过边的数量,
当cnt>=n说明有环,cnt<n无环
加入cnt[];
public static boolean spfahuan()
{
l.add(new Edge(s,0));
vis[s]=true;//表示在队列里
while(!l.isEmpty())
{
Edge t=l.poll();
vis[t.to]=false;
for(Edge next:map[t.to])
{
if(dis[t.to]+next.w<dis[next.to])
{
dis[next.to]=dis[t.to]+next.w;
cnt[next.to]=cnt[t.to]+1;
if(cnt[next.to]>=n) return true;
if(vis[next.to]) continue;
l.add(next);
vis[next.to]=true;
}
}
}
return false;
}
一般模板:求1到n的最短距离
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define INF 0x7f7f7f7f
const int N=1e5+5;
int h[N], w[N], e[N], ne[N], idx;
//void add(int a, int b, int c)//起点a 终点b 权值c
//{
// e[idx] = b, w[idx] = c, ne[idx] = h[a], h[a] = idx ++;
//}
void add(int a,int b,int c)
{
e[idx]=b;w[idx]=c;ne[idx]=h[a];h[a]=idx++;
}
int dis[200000];
bool vis[200000];
int n,m;
void spfa(int x)
{
fill(dis,dis+200000,INF);
dis[x]=0;
queue<int> q;
q.push(x);
vis[x]=true;
while(!q.empty())
{
int now=q.front();q.pop();
vis[now]=false;
for(int i=h[now];i!=-1;i=ne[i])
{
int next=e[i];
if(dis[now]+w[i]<dis[next])
{
dis[next]=dis[now]+w[i];
if(vis[next]) continue;
vis[next]=true;
q.push(next);
}
}
}
}
int main()
{
cin>>n>>m;
fill(h,h+N,-1);
for(int i=0;i<m;i++)
{
int x,y,z;
cin>>x>>y>>z;
add(x,y,z);
}
spfa(1);
if(dis[n]==INF) cout<<"impossible";
else cout<<dis[n];
return 0;
}
例题:
P3371 【模板】单源最短路径(弱化版)
package shortestpath;
//2021年3月25日下午3:34:10
//writer:apple
import java.io.*;
import java.util.ArrayList;
import java.util.LinkedList;
public class Spfa {
static int n;
static int m;
static int s;
static class Edge{
int to;
int w;
public Edge(int ttt,int www)
{
to=ttt;w=www;
}
}
static boolean vis[];
static int dis[];
static ArrayList<Edge> map[];
static LinkedList<Edge> l=new LinkedList<>();
public static void main(String[] args) throws IOException{
// TODO Auto-generated method stub
StreamTokenizer st=new StreamTokenizer(new BufferedReader(new InputStreamReader(System.in)));
st.nextToken();n=(int)st.nval;
st.nextToken();m=(int)st.nval;
st.nextToken();s=(int)st.nval;
//
map=new ArrayList[n+1];
vis=new boolean[n+1];
dis=new int[n+1];
for(int i=0;i<=n;i++)
{
map[i]=new ArrayList<>();
dis[i]=Integer.MAX_VALUE;
vis[i]=false;
}
dis[s]=0;
for(int i=0;i<m;i++)
{
st.nextToken();int a=(int)st.nval;
st.nextToken();int b=(int)st.nval;
st.nextToken();int c=(int)st.nval;
map[a].add(new Edge(b,c));
}
spfa();
for(int i=1;i<=n;i++)
{
System.out.print(dis[i]+" ");
}
}
public static void spfa()
{
l.add(new Edge(s,0));
vis[s]=true;//表示在队列里
while(!l.isEmpty())
{
Edge t=l.poll();
vis[t.to]=false;
for(Edge next:map[t.to])
{
if(dis[t.to]+next.w<dis[next.to])
{
dis[next.to]=dis[t.to]+next.w;
if(vis[next.to]) continue;
l.add(next);
vis[next.to]=true;
}
}
}
}
}
#include<bits/stdc++.h>
#include<iostream>
using namespace std;
const double eps = 1e-8; //const只是用来定义常量,常量在代码中一直存在,但define是预处理功能,有本质区别
const int maxx = 0x7f7f7f7f;//0x7f7f7f7f表示数据的无穷大
//常用的浮点数比较宏定义:
#define Equ(a,b) ((fabs((a)-(b)))<(eps)) //等于
#define More(a,b) (((a)-(b))>(esp)) //大于
#define Less(a,b) (((a)-(b))<(-esp))//小于
#define MoreEqu(a,b) (((a)-(b))>(-esp))//大于等于
#define LessEqu(a,b) (((a)-(b))<(esp))//小于等于
#define MAX( x, y ) ( ((x) > (y)) ? (x) : (y) )//
//使用了algorithm头文件就可以直接使用max函数;
#define MIN( x, y ) ( ((x) < (y)) ? (x) : (y) )
#define ll long long
#define PI 3.1415926
#define eps 1e-8
#define Conn(x,y) x##y;
int n,m;
int sta;
int dis[100005];
bool vis[100005];
struct node
{
int to,w;
node(int tt,int ww):to(tt),w(ww){
}
};
vector<node> mapp[100005];
void spfa(int x)
{
fill(dis,dis+100005,maxx);
dis[x]=0;
queue<node> q;
q.push(node(x,0));
vis[x]=true;
while(!q.empty())
{
node now=q.front();q.pop();
vis[now.to]=true;
for(int i=0;i<mapp[now.to].size();i++)
{
int next=mapp[now.to][i].to;
int nextw=mapp[now.to][i].w;
if(dis[now.to]+nextw<dis[next])
{
dis[next]=dis[now.to]+nextw;
if(vis[next]) continue;
vis[next]=true;
q.push(node(next,nextw));
}
}
}
}
int main()
{
cin>>n>>m>>sta;
for(int i=1;i<=m;i++)
{
int x,y,l;
cin>>x>>y>>l;
mapp[x].push_back(node(y,l));
mapp[y].push_back(node(x,l));
}
spfa(sta);
cout<<dis[1];
for(int i=2;i<=n;i++)
{
cout<<" "<<dis[i];
}
return 0;
}
Bellman-Ford(贝尔曼福特)算法
适用范围:
单源最短路径
对于有负权的图,可以完美解决求最短路径问题。
使用有向图,无向图,边权可正可负
可以判负环
思想:O(NM)
一直对边进行松弛,直到结束
一共需要N-1次松弛
1,第一轮松弛,对每一条边进行松弛
2,第二轮,重复松弛,直到结束
操作代码
1,建立边Edge[]
2,n-1轮松弛
3,判断是否环
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define INF 0x7f7f7f7f
const int N=1e5+5;
int dis[N];
int backup[N];
int n,m,k;
struct edge{
int a,b,w;
}e[N];
bool bellman(int x)
{
fill(dis,dis+N,INF);
dis[x]=0;
for(int i=1;i<=k;i++)
{
memcpy(backup,dis,sizeof(dis));//在限制了一个路径中最多k条边 的时候采用备份数组,防止串联
for(int j=0;j<m;j++)
{
dis[e[j].b]=min(dis[e[j].b],backup[e[j].a]+e[j].w);
}
}
if(dis[n]>INF/2) return false;
return true;
}
int main()
{
cin>>n>>m>>k;
for(int i=0;i<m;i++)
{
int x,y,z;
cin>>x>>y>>z;
e[i].a=x;e[i].b=y;e[i].w=z;
}
if(bellman(1)) cout<<dis[n];
else cout<<"impossible";
return 0;
}
package shortestpath;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.StreamTokenizer;
//2021年3月25日下午6:01:28
//writer:apple
public class Bellman_ford {
static int n;
static int m;
static int s;
static int dis[];
static class Edge{
int f,t,w;
// public Edge()
// {
//
// }
public Edge(int from,int to,int weight)
{
f=from;t=to;w=weight;
}
}
static Edge edge[];
public static void main(String[] args) throws IOException {
// TODO Auto-generated method stub
StreamTokenizer st=new StreamTokenizer(new BufferedReader(new InputStreamReader(System.in)));
st.nextToken();n=(int)st.nval;
st.nextToken();m=(int)st.nval;
st.nextToken();s=(int)st.nval;
dis=new int[n+1];
edge=new Edge[m+1];
for(int i=0;i<=n;i++) dis[i]=Integer.MAX_VALUE;
dis[s]=0;
for(int i=1;i<=m;i++)
{
st.nextToken();int from=(int)st.nval;
st.nextToken();int to=(int)st.nval;
st.nextToken();int weight=(int)st.nval;
if(from==s) dis[to]=weight;
edge[i]=new Edge(from,to,weight);
}
if(bellman_ford())
{
for(int i=1;i<=n;i++)
{
System.out.print(dis[i]+" ");
}
}
}
public static boolean bellman_ford()
{
for(int i=1;i<n;i++)//n-1轮 松弛
{
for(int j=1;j<=m;j++)//共m条边
{
Edge temp=edge[j];
if(dis[temp.f]+temp.w<dis[temp.t])
{
dis[temp.t]=dis[temp.f]+temp.w;
}
}
}
boolean flag=true;
for(int i=1;i<=m;i++)
{
Edge temp=edge[i];
if(dis[temp.f]+temp.w<dis[temp.t])
{
flag=false;break;
}
}
return flag;
}
}
例题
1,洛谷P1744