来自AcWing的y总
- Dijkstra伪代码(不能有负权边)
朴素算法:主要针对稠密图 时间复杂度 O ( n 2 ) O(n^2) O(n2)
- 初始化起点的距离为dist[1]=0, 其他点为dist[…n]= ∞ \infty ∞
- 用一个集合s存储已经确定最短距离的点
for i:0~n
t<—找到不在s中距离最短的点t
s<—t将t加入s
用t更新不在s中的其他点的距离(dis[x]>dis[t]+w)考虑点t的出边
Dijkstra求最短路 I:https://www.acwing.com/problem/content/851/
#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;
const int N=505;
int g[N][N],dis[N];
bool st[N];
int n,m;
int Dijkstra(){
memset(dis,0x3f,sizeof dis);
dis[1]=0;
for(int i=0;i<n-1;i++){
int t=-1;
for(int j=1;j<=n;j++)
if(!st[j]&&(t==-1||dis[t]>dis[j]))
t=j;
for(int j=1;j<=n;j++)
dis[j]=min(dis[t]+g[t][j],dis[j]);
st[t]=true;
}
if(dis[n]==0x3f3f3f3f) return -1;
return dis[n];
}
int main(){
cin>>n>>m;
memset(g,0x3f,sizeof g);
while(m--){
int x,y,z;
cin>>x>>y>>z;
g[x][y]=min(z,g[x][y]); //处理重边
}
cout<<Dijkstra();
return 0;
}
堆优化版:主要针对稀疏图 时间复杂度 O ( m l o g 2 n ) O(mlog_2^n) O(mlog2n)
找到最近的点——>用堆来优化 O(1)
更新最近点的所有出边,若不在堆中就可能是下一次的最近点——>插入堆中 O(logn)
注:这一步要用st数组进行标记,判断更新后的点是否已经用过了
Dijkstra求最短路II:https://www.acwing.com/problem/content/852/
//针对稀疏图 复杂度:mlogn
#include<iostream>
#include<cstring>
#include<algorithm>
#include<queue>
using namespace std;
const int N=1.5e5+5;
int h[N], w[N], e[N], ne[N], idx;
int dist[N];
bool st[N];
int n,m;
typedef pair<int, int> PII;
void add(int a, int b, int c){
e[idx]=b, w[idx]=c, ne[idx]=h[a], h[a]=idx++;
}
int Dijkstra(){
memset(dist,0x3f,sizeof dist);
priority_queue<PII,vector<PII>,greater<PII>> heap;
heap.push({0,1});
dist[1]=0; //一定得初始化第一个点距离为0
while(heap.size()){ //bfs队列
auto t=heap.top();
heap.pop();
int ver=t.second, distance=t.first;
if(st[ver]) continue;
st[ver]=true;
for(int i=h[ver];i!=-1;i=ne[i]){ //用邻接矩阵矩阵存
int j=e[i];
if(dist[j]>dist[ver]+w[i]){ //有m条边,
dist[j]=dist[ver]+w[i]; //每次更新后插入堆中复杂度为logn
heap.push({dist[j],j});
}
}
}
if(dist[n]==0x3f3f3f3f) return -1;
return dist[n];
}
int main(){
cin>>n>>m;
memset(h,-1,sizeof h);
while(m--){
int a, b,c;
scanf("%d%d%d",&a,&b,&c);
add(a,b,c);
}
cout<<Dijkstra();
return 0;
}
- Bellman_Ford算法
描述:从源点起,走不超过k条边能到达的点的最短距离 (有边数限制)
伪代码
for i: 0~k
for j: 0~m
dist[b]=min(dist[b],dist[a]+w[j]);
有边数限制的最短路:https://www.acwing.com/problem/content/855/
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
int n,m,k;
const int N=1e5+5;
int dist[N], backup[N];
struct Edge{ //bellman_Ford 可以直接用struct 来存边
int a,b,c;
}edge[N];
int bellman_ford(){
memset(dist,0x3f,sizeof dist);
dist[1]=0;
for(int i=0;i<k;i++){ //从起始点起走不超过k条边
memcpy(backup,dist,sizeof dist); //备份是为了本次的更新只能用上一次dist结果
for(int j=0;j<m;j++){
int a=edge[j].a, b=edge[j].b ,c=edge[j].c;
dist[b]=min(dist[b],backup[a]+c); //用上一层的点进行更新
} //避免出现串联的情况
}
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,c;
scanf("%d%d%d",&a,&b,&c);
edge[i]={a,b,c};
}
int t=bellman_ford();
if(t==-1)puts("impossible");
else printf("%d\n",t);
return 0;
}
- spfa算法
思路:运用队列对Bellman_Ford算法进行优化,只有更新了距离的点才有能力更新它的出边。
spfa算法求最短路:https://www.acwing.com/problem/content/853/
#include<iostream>
#include<cstring>
#include<algorithm>
#include<queue>
using namespace std;
const int N=1.5e5+5;
int h[N], w[N], e[N], ne[N], idx;
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 spfa(){
memset(dist,0x3f,sizeof dist);
dist[1]=0;
queue<int> q;
q.push(1);
while(q.size()){
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]){
dist[j]=dist[t]+w[i];
if(!st[j]){
q.push(j);
st[j]=true;
}
}
}
}
if(dist[n]==0x3f3f3f3f) return -1;
else return dist[n];
}
int main(){
cin>>n>>m;
memset(h,-1,sizeof h);
while(m--){
int a, b,c;
scanf("%d%d%d",&a,&b,&c);
add(a,b,c);
}
int t=spfa();
if(t==-1) puts("impossible");
else printf("%d",t);
return 0;
}
spfa判断负环:https://www.acwing.com/problem/content/854/
#include<queue>
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
const int N=1e5+5;
int h[N], e[N],ne[N], w[N], idx;
using namespace std;
int dist[N], cnt[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++;
}
bool spfa(){
queue<int> q;
for(int i=1;i<=n;i++){ //将所有点都入队进行判断,如果只将起点入队可能不经过负环
q.push(i);
st[i]=true;
}
while(q.size()){
int t=q.front();
q.pop();
st[t]=0;
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 true;
if(!st[j]){
q.push(j);
st[j]=1;
}
}
}
}
return false;
}
int main(){
memset(h,-1,sizeof h);
scanf("%d%d",&n,&m);
while(m--){
int a,b,c;
scanf("%d%d%d",&a,&b,&c);
add(a,b,c);
}
if(spfa()) puts("Yes");
else puts("No");
return 0;
}
- Floyd算法(多源汇最短路):
Floyd求最短路:https://www.acwing.com/activity/content/problem/content/923/1/
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
using namespace std;
const int N=205;
const int INF=1e9;
int d[N][N];
int n,m,k;
void floyd(){
for(int k=1;k<=n;k++)
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
d[i][j]=min(d[i][j],d[i][k]+d[k][j]);
}
int main(){
cin>>n>>m>>k;
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
if(i==j)d[i][j]=0;
else d[i][j]=INF;
while(m--){
int a,b,c;
cin>>a>>b>>c;
d[a][b]=min(d[a][b],c); //这个存在自环并且权值可能为负
}
floyd();
while(k--){
int x,y;
cin>>x>>y;
if(d[x][y]>INF/2)puts("impossible");
else printf("%d\n",d[x][y]);
}
return 0;
}