最短路分为单源最短路和多源汇最短路;
1单源最短路根据边长的正负分为两类(n表示点,m表示边)
(1)边长为正
dijkstra算法
朴素版(o(n^2))
堆优化版(0(mlogn))
当稠密图(m>=n^2)时朴素版的时间更优,稀疏图则用堆优化版更优;
(2)边权有负
bellman_ford算法 0(nm)
由于SPFA算法是bellman_ford的优化,bellman_ford常用于求有边数限制的最短路,如在不经过超过k条边时的最短路,注意这时的不存在最短路的临界条件是dist[n]>0x3f3f3f3f,因为可能存在,dist[t]=0x3f3f3f3f,但存在t到n的边可能松弛dist[n];
SPFA O(m),最坏O(nm)
还可用来求负环,也可以用来求正权最短路
2.多源汇最短路
folyd算法 0(n^3) 适合稠密图
dijkstra算法朴素版
#include<iostream>
#include<cstring>
using namespace std;
int g[510][510];
int n,m;
int vis[510],dist[510];
int dijkstra()
{
memset(dist,0x3f,sizeof dist);
dist[1]=0;
for(int i=0;i<n;i++)
{
int t=-1;
for(int j=1;j<=n;j++)
{
if(!vis[j]&&(t==-1||dist[t]>dist[j]))
t=j;
}
vis[t]=1;
for(int j=1;j<=n;j++)
dist[j]=min(dist[j],dist[t]+g[t][j]);
}
if(dist[n]==0x3f3f3f3f)return -1;
return dist[n];
}
int main()
{
memset(g,0x3f,sizeof g);
cin>>n>>m;
while(m--)
{
int a,b,c;
cin>>a>>b>>c;
g[a][b]=min(g[a][b],c);
//g[b][a]=min(g[b][a],c);
}
cout<<dijkstra()<<endl;
return 0;
}
堆优化版
#include<iostream>
#include<cstring>
#include<algorithm>
#include<queue>
using namespace std;
typedef pair<int,int> PII;
const int N=100010;
int h[N],e[N],ne[N],idx,w[N];
int dist[N];
bool st[N];
int n,m;
void add(int a,int b,int c){
e[idx]=b;
w[idx]=c;
ne[idx]=h[a];
h[a]=idx++;
}
int dijktra(){
memset(dist,0x3f,sizeof dist);
dist[1]=0;
priority_queue<PII,vector<PII>,greater<PII>>heap;
heap.push({0,1});
while(heap.size()){
auto t=heap.top();
heap.pop();
int ver=t.second,distance=t.first;
if(distance>dist[ver])continue;
for(int i=h[ver];i!=-1;i=ne[i]){
int j=e[i];
if(dist[j]>distance+w[i])
{
dist[j]=distance+w[i];
heap.push({dist[j],j});
}
}
}
if(dist[n]==0x3f3f3f3f)return -1;
return dist[n];
}
int main(){
memset(h,-1,sizeof h);
cin>>n>>m;
while(m--){
int a,b,c;
cin>>a>>b>>c;
add(a,b,c);
}
cout<<dijktra()<<endl;
return 0;
}
bellman_ford求有边数限制
#include<iostream>
#include<queue>
#include<cstring>
using namespace std;
int n,m,k;
int dist[510],bakcup[510];
struct Edge
{
int a,b,w;
}edge[10010];
int bellmen_ford()
{
memset(dist,0x3f,sizeof dist);
dist[1]=0;
for(int i=0;i<k;i++)
{
memcpy(bakcup,dist,sizeof dist);//拷贝dist数组到bakcup防止发生串联
for(int j=0;j<m;j++)
{
int a=edge[j].a,b=edge[j].b,w=edge[j].w;
dist[b]=min(dist[b],bakcup[a]+w);//对每一条边a->b进行松弛
}
}
if(dist[n]>0x3f3f3f3f/2)return -1;
return dist[n];
}
int main()
{
cin>>n>>m>>k;
for(int i=0;i<m;i++)
{
int a,b,w;
cin>>a>>b>>w;
edge[i]={a,b,w};
}
int t=bellmen_ford();
if(t==-1)printf("impossible\n");
else
printf("%d\n",dist[n]);
return 0;
}
SPEA求最短路(前提是不存在负环)
#include<iostream>
#include<cstring>
#include<queue>
using namespace std;
int h[100010],ne[100010],e[100010],w[100010],idx;
int dist[100010],st[100010];int n,m;
void add(int a,int b,int c)
{
e[idx]=b,ne[idx]=h[a],w[idx]=c,h[a]=idx++;
}
int spea()
{
memset(dist,0x3f,sizeof dist);
dist[1]=0;
queue<int>q;
st[1]=1;q.push(1);
while(q.size())
{
int t=q.front();
q.pop();
st[t]=0;//t出队
for(int i=h[t];i!=-1;i=ne[i])
{
int j=e[i];
if(dist[j]>dist[t]+w[i])
{
dist[j]=dist[t]+w[i];
if(!st[j])
{
q.push(j);
st[j]=1;
}
}
}
}
if(dist[n]==0x3f3f3f3f)return -1;//完全做完后一定为最小值
return dist[n];
}
int main()
{
cin>>n>>m;
memset(h,-1,sizeof h);
while(m--)
{
int a,b,w;cin>>a>>b>>w;
add(a,b,w);
}
int t=spea();
if(t==-1)printf("impossible\n");
else
printf("%d\n",t);
return 0;
}
SPFA判断负环
#include<iostream>
#include<cstring>
#include<queue>
using namespace std;
int h[100010],ne[100010],e[100010],w[100010],idx;
int dist[100010],st[100010];int n,m,cnt[100010];
void add(int a,int b,int c)
{
e[idx]=b,ne[idx]=h[a],w[idx]=c,h[a]=idx++;
}
int spea()
{
memset(dist,0x3f,sizeof dist);
dist[1]=0;
queue<int>q;
for(int i=1;i<=n;i++)
st[i]=1,q.push(i);//负环不仅在1开始的路径上
while(q.size())
{
int t=q.front();
q.pop();
st[t]=0;//t出队
for(int i=h[t];i!=-1;i=ne[i])
{
int j=e[i];
if(dist[j]>dist[t]+w[i])
{
dist[j]=dist[t]+w[i];
cnt[j]=cnt[t]+1;//该路径多了一条边
if(cnt[j]>=n)return 1;//抽屉原理,经过n条边,即经过n+1个点,必有两个相同的点
if(!st[j])
{
q.push(j);
st[j]=1;
}
}
}
}
return 0;
}
int main()
{
cin>>n>>m;
memset(h,-1,sizeof h);
while(m--)
{
int a,b,w;cin>>a>>b>>w;
add(a,b,w);
}
int t=spea();
if(t)printf("Yes\n");
else
printf("No");
return 0;
}
folyd
#include<iostream>
#include<cstring>
using namespace std;
int g[510][510];
int main()
{
memset(g,0x3f,sizeof g);
int n,m,k;cin>>n>>m>>k;
for(int i=1;i<=n;i++)g[i][i]=0;
while(m--)
{
int a,b,c;
cin>>a>>b>>c;
g[a][b]=min(c,g[a][b]);
}
for(int k=1;k<=n;k++)
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
g[i][j]=min(g[i][j],g[i][k]+g[k][j]);
while(k--)
{
int a,b;
cin>>a>>b;
if(g[a][b]>0x3f3f3f3f/2)printf("impossible\n");
else
printf("%d\n",g[a][b]);
}
return 0;
}