说明:n 为点数 m 为边数
单源最短路
所有边都是正数
dijkstra()算法(On^2)
Acwing 849 朴素版作法(适合稠密图 与边数无关)用邻接矩阵写
模板题从1 号点 到 n 号点最短路
#include<iostream>
#include<algorithm>
#include<cstring> //使用 memset 函数
using namespace std;
const int N=510;
int g[N][N],dist[N];
bool st[N];
int n,m;
int dijstra()
{
memset(dist,0x3f,sizeof(dist));
dist[1]=0;
for(int i=0;i<n;i++) //其实就找了n-1次就可以 最后一个点不用更新也是最短的点
{
int t=-1;
for(int j=1;j<=n;j++) //每次先选出当前点中最短的点
{
if(!st[j]&&(t==-1||dist[t]>dist[j]))
t=j;
}
st[t]=true; //已经确定是最短点
for(int j=1;j<=n;j++) //用当前确定的点去更新其他点
dist[j]=min(dist[j],dist[t]+g[t][j]);
}
//memset会把每个字节都写成0x3f,dist[n]是int型,包含4个字节,所以一定要写成0x3f3f3f3f
if(dist[n]==0x3f3f3f3f) return -1;
return dist[n];
}
int main()
{
cin >>n >>m;
memset(g,0x3f,sizeof(g));
while(m--)
{
int a,b,c;
cin >>a >>b >>c;
g[a][b]=min(c,g[a][b]);
}
int t=dijstra();
cout <<t<<endl;
return 0;
}
堆优化的dijkstra() O(mlogn)
Acwing 850 适用于(稀疏图)O(mlogn)
#include<iostream>
#include<algorithm>
#include<cstring>
#define N 150005
#include<queue>
using namespace std;
int n,m;
int dist[N];
bool st[N];
int h[N],w[N],e[N],ne[N],idx; // 邻接表 的表示
void add(int a,int b,int c)
{
w[idx]=c,e[idx]=b,ne[idx]=h[a],h[a]=idx,idx++;
}
typedef pair<int,int>PII;
//优先队列的使用方法 小根堆得到最小值定义名称为 heap
priority_queue< PII,vector<PII>,greater<PII> >heap;
int dijkstra()
{ //这里queue的使用方法套路一致
memset(dist,0x3f,sizeof(dist));
dist[1]=0;
heap.push({0,1}); **//存储时first指的是路径距离second指的是结点**
while(!heap.empty())
{
PII t = heap.top();
heap.pop();
int a = t.second,b = t.first; **//这里的a和朴素版的t 是一个作用**
if(st[a]) continue; //每次都是集合中的最短路只用一次
st[a]=true;
for(int i=h[a];i!=-1;i=ne[i]) //邻接表的模板
{
int j=e[i];//此时i代表的是当前节点的开始位置
//j代表的是该节点的结束位置,w[i]该节点的路径长度
if(dist[j]>b+w[i])
{
dist[j]= b+w[i]; //这里的w[i]指的是朴素版的g[i][j]存的是两点的距离;
heap.push({dist[j],j}); //还可能需要更新
}
}
}
//memset会把每个字节都写成0x3f,dist[n]是int型,包含4个字节,所以一定要写成0x3f3f3f3f
if(dist[n]==0x3f3f3f3f) return -1;
return dist[n];
}
int main()
{
cin >>n >>m;
memset(h,-1,sizeof(h));
while(m--)
{
int x,y,z;
cin >>x >>y >>z;
add(x,y,z);
}
cout <<dijkstra()<<endl;
return 0;
}
存在负权边
bellman-ford算法 O(nm)
有边数限制的最短路
用结构体来存数;
Acw 853.有边数限制的最短路;
#include<iostream>
#include<algorithm>
#include<cstring>
const int N =505,M=10010;
using namespace std;
int n,m,k;
int dist[N],backup[N];
struct edge{
int a,b,w;
}st[M];
int bellman_ford()
{
memset(dist,0x3f,sizeof(dist));
dist[1]=0;
for(int i=0;i<k;i++) //进行限制性的K条边的操作
{
memcpy(backup,dist,sizeof(dist)); //有限制 要用上一轮的数组来比较,注意写法
for(int j=0;j<m;j++) //枚举的是每一条边
{ //这里是m 个结构体每个结构体的 a,b,c;
int a=st[j].a,b=st[j].b,w=st[j].w;
dist[b]=min(dist[b],backup[a]+w); //用上一轮的最小值更新因为这一轮可能会串联
}
}
//存在负环到不了正无穷 这里除2是最保险的;
if(dist[n]>0x3f3f3f3f/2) return -1;
return dist[n];
}
int main()
{
scanf("%d%d%d",&n,&m,&k);
for(int i=0;i<m;i++)
{
int a,b,w;
scanf("%d%d%d",&a,&b,&w);
st[i]={a,b,w};
}
int t=bellman_ford();
if(t==-1) puts("impossible");
else printf("%d\n",t);
return 0;
}
spfa()算法 (一般O(m)最坏是O(nm))
Acwing 851 这里也是队列实现,不是最优队列
绝大多数 dijkstra()都能用spfa() 做 也是用邻接链表做
#include<iostream>
#include<cstring>
#include<queue>
using namespace std;
const int N =2e5;//按边数来存
int n,m;
int dist[N];
bool st[N];
int h[N],w[N],e[N],ne[N],idx;
void add(int a,int b,int c)
{
w[idx]=c,e[idx]=b,ne[idx]=h[a],h[a]=idx,idx++;
}
int spfa()
{
memset(dist,0x3f,sizeof(dist));
dist[1]=0;
queue<int>q;
q.push(1);
st[1]=true; //当前这个点是不是在队列中 其实也不用写
while(!q.empty())
{
int t=q.front();
q.pop();
st[t] = false; //已经不在队列之中
for(int i=h[t];i!=-1;i=ne[i])
{
int j=e[i]; //用 t 这个点看是不是能当前这个点
if(dist[j]>dist[t]+w[i])
{
dist[j]=dist[t]+w[i];
q.push(j);
//没有这个判断好像也行
if(!st[j]) //不在队列里
{
q.push(j);
st[j]=true; //这里只是不会让他重复进队 重进的话顶多不更新
}
}
}
}
if(dist[n]==0x3f3f3f3f) return -1;
return dist[n];
}
int main()
{
cin >>n>>m;
memset(h,-1,sizeof(h));
while(m--)
{
int x,y,z;
cin >>x>>y >>z;
add(x,y,z);
}
int t=spfa();
if(t==-1) cout <<"impossible";
else cout <<spfa();
return 0;
}
用spfa()判断负环
用一个cnt[ ]数组来维护
#include<iostream>
#include<cstring>
#include<algorithm>
#define N 100005
#include<queue>
using namespace std;
int n,m;
int dist[N],cnt[N];
bool st[N];
int h[N],w[N],e[N],ne[N],idx;
int add(int a,int b,int c)
{
w[idx]=c,e[idx]=b,ne[idx]=h[a],h[a]=idx,idx++;
}
int spfa()
{
queue<int>q;
for(int i=1;i<=n;i++)//所有点全都放那如入
{
st[i]=true;
q.push(i);
}
while(!q.empty())
{
int t=q.front();
q.pop();
st[t] = false;
for(int i=h[t];i!=-1;i=ne[i])
{
int j=e[i];
if(dist[j]>dist[t]+w[i])
{
cnt[j]=cnt[t]+1;
if(cnt[j]>=n) return true;
dist[j]=dist[t]+w[i];
if(!st[t])
{
q.push(j);
st[j]=true;
}
}
}
}
return false;
}
int main()
{
cin >>n>>m;
memset(h,-1,sizeof(h));
while(m--)
{
int x,y,z;
cin >>x>>y >>z;
add(x,y,z);
}
if(spfa()) puts("Yes");
else puts("No");
return 0;
}
多源汇最短路
Floyd算法(O(n^3))
题目
给定一个N个点m条变的有向图,可能存在重边和自环,边权可能为负数。
再给定K个询问 输出两点之间的最短距离;
输入(此题为稠密图)
第一行n,m,k;
接下来m行 每行x,y,z; 表示点x到点y之间存在一条有向边边长为z;
接下来k行 每行a,b, 表示 询问 点a到点b之间的最短距离
输出
共K行每行输出一个整数表示询问的结果,若询问的两点之间不存在路径,输出 impossbile
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
const int N =210,INF=1e9;
int n,m,q;
int d[N][N];
void floyd() //结束之后 d[i,j]存的就是最短距离
{
for(int k=1;k<=n;k++)
for(int i=1;i<=n;i++) //从 i 这个点经过 1~k 这么多点到 j 的最短距离
for(int j=1;j<=n;j++)
d[i][j]=min(d[i][j],d[i][k]+d[k][j]);
}
int main()
{
scanf("%d%d%d",&n,&m,&q);
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
if(i==j)d[i][j]=0;
else d[i][j]=INF;
while(m--)
{
int a,b,w;
scanf("%d%d%d",&a,&b,&w);
d[a][b]=min(d[a][b],w);
}
floyd();
while(q--)
{
int a,b;
scanf("%d%d",&a,&b);
if(d[a][b]>INF/2)puts("impossbile");
else printf("%d\n",d[a][b]);
}
return 0;
}