图论最短路复习
朴素Dijkstra
https://www.acwing.com/problem/content/description/851/
O(N^2)
#include<iostream>
#include<algorithm>
#include<cstring>
#include<cstdio>
using namespace std;
const int INF = 0x3f3f3f3f;
const int N = 510;
int g[N][N];//边
int dist[N];//最短距离
bool st[N];
int n,m;
int dijkstra()
{
memset(dist,INF,sizeof dist);
dist[1]=0;
for(int i=0;i<n;i++)
{
int t=-1;
for(int j=1;j<=n;j++)//寻找距离起点最短的t点
if(!st[j] && (t==-1 || dist[t]>dist[j]))
t=j;
if(t==n)break;//优化,t直接是终点,则直接找到了最短路
st[t]=true;
for(int j=1;j<=n;j++)
dist[j]=min(dist[j],dist[t]+g[t][j]);//用t更新其它点的距离
}
if(dist[n]==INF)return -1;//1与n不连通
else return dist[n];
}
int main()
{
cin >> n >> m;
memset(g,INF,sizeof g);
for(int i=0;i<m;i++)
{
int x,y,z;
cin >> x >> y >> z;
g[x][y]=min(g[x][y],z);
}
int t=dijkstra();
cout << t << endl;
}
对于稀疏图(n>1e5),采用堆优化版Dijkstra( O(mlogn) )
这里使用优先队列实现
https://www.acwing.com/problem/content/description/852/
#include<iostream>
#include<algorithm>
#include<cstring>
#include<cstdio>
#include<queue>
using namespace std;
typedef pair<int,int>PII;
const int N = 1e6+10;
int h[N],e[N],ne[N],w[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 dijkstra()
{
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(st[ver])continue;//ver此前已经出现过
st[ver]=true;
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;//1与n不连通
else return dist[n];
}
int main()
{
cin >> n >> m;
memset(h,-1,sizeof h);
for(int i=0;i<m;i++)
{
int x,y,z;
cin >> x >> y >> z;
add(x,y,z);
}
int t=dijkstra();
cout << t << endl;
}
Bellman-Ford算法求最短路(O(NM))
https://www.acwing.com/problem/content/855/
Bellman-Ford对于边很宽容,用一个结构体存储即可,它只需要能够被访问即可
//Bellman-Ford算法求含负权边最短路
//若图中存在负权回路则最短路不一定存在(负环在最短路径上,则最短路会变成负无穷,不若在最短路径上,则存在最短路)
//O(nm)
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int N = 510,M=10010;
int dist[N],backup[N];
struct Edge
{
int a,b,w;
}edges[M];
int n,m,k;
int bellman_ford()
{
memset(dist,0x3f,sizeof dist);//初始化
dist[1]=0;//初始化
for(int i=0;i<k;i++)
{
memcpy(backup,dist,sizeof dist);
for(int j=0;j<m;j++)
{
int a=edges[j].a,b=edges[j].b,w=edges[j].w;
dist[b]=min(dist[b],backup[a]+w);
}
}
return dist[n];
}
int main()
{
cin >> n >> m >> k;
for(int i=0;i<m;i++)
{
int a,b,w;
cin >> a >> b >> w;
edges[i]={a,b,w};
}
int t=bellman_ford();
if(t>0x3f3f3f3f/2)cout << "impossible" << endl;
else cout << t << endl;
}
spfa求含负权边最短路(O(m),最坏O(nm))
https://www.acwing.com/problem/content/853/
#include<iostream>
#include<algorithm>
#include<cstring>
#include<cstdio>
#include<queue>
using namespace std;
typedef pair<int,int>PII;
const int N = 1e6+10;
int h[N],e[N],ne[N],w[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);
st[1]=true;//st防止存储重复点
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;
}
}
}
}
return dist[n];
}
int main()
{
cin >> n >> m;
memset(h,-1,sizeof h);
for(int i=0;i<m;i++)
{
int x,y,z;
cin >> x >> y >> z;
add(x,y,z);
}
int t=spfa();
if(t==0x3f3f3f3f)cout << "impossible" <<endl;
else cout << t << endl;
}
spfa判断负环
https://www.acwing.com/problem/content/description/854/
#include<iostream>
#include<algorithm>
#include<cstring>
#include<cstdio>
#include<queue>
using namespace std;
typedef pair<int,int>PII;
const int N = 1e6+10;
int h[N],e[N],ne[N],w[N],idx;//邻接表存储
int cnt[N];//维护一个表示从起点走到i的经过的边数的数组,一旦cnt[x]>=n,则表示在1~x中存在负环
//cnt[x]>=n,则表示1~x个点,却经历了n条边,那么根据抽屉原理一定存在重复点
//重复点就说明存在环,由于算法是更新更加短的路,存在环那么总路径一定会变短,变短就说明是负环
//若是正环,那么dist必定增加,不会选择走环,边数最多为n-1
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++;
}
bool spfa()
{
// 因为只需要判断负环,所以无需初始化了
queue<int>q;
// 队列中不只是需要第一个点了,而是需要所有点,因为单从第一个点不一定能走到负环
for(int i=1;i<=n;i++)
{
st[i]=true;
q.push(i);
}
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];
cnt[j]=cnt[t]+1;
if(cnt[j]>=n)return true;
if(!st[j])
{
q.push(j);
st[j]=true;
}
}
}
}
return false;
}
int main()
{
cin >> n >> m;
memset(h,-1,sizeof h);
for(int i=0;i<m;i++)
{
int x,y,z;
cin >> x >> y >> z;
add(x,y,z);
}
if(spfa())cout <<"Yes" << endl;
else cout << "No" << endl;
}
floyd多源汇最短路(O(N^3))
https://www.acwing.com/problem/content/856/
#include<iostream>
#include<cstring>
#include<algorithm>
#include<cstdio>
using namespace std;
const int INF = 0x3f3f3f3f;
const int N = 210;
int n,m,k,Q;
int d[N][N];//算法执行前是领接矩阵,执行后是点之间的最短距离
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 >> Q;
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,w;
cin >> a >> b >> w;
d[a][b]=min(d[a][b],w);//多条边只保留最小边
}
floyd();
while(Q--)
{
int a,b;
cin >> a>> b;
//>INF/2的原因是若存在负权边,尽管两点之间不连通,却仍然会更新距离,会稍微比INF小一些,导致不连通却不等于INF
if(d[a][b] > INF/2)cout << "impossible" << endl;
else cout << d[a][b] << endl;
}
}
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· 没有源码,如何修改代码逻辑?
· NetPad:一个.NET开源、跨平台的C#编辑器
· PowerShell开发游戏 · 打蜜蜂
· 凌晨三点救火实录:Java内存泄漏的七个神坑,你至少踩过三个!