最短路径算法模板
前言
这篇文章的讲解比较侧重于实践应用 (主要是为了存板子)
首先来一张学长的ppt:
但是我太弱了 所以只搞了3种板子 :spfa dijkstra floyd
正权边:dijkstra (有一种玄学优化spfa在很多题上也表现不凡 还没找到卡的办法 下面会展开讲
负权边:spfa及其优化 或者floyd
多源点:floyd
dijkstra
(由于我实在是太懒)
(而且前人的记述很详尽了)
(所以 直接甩链接)
这篇博客解释地非常详细而且有趣
比我讲的要好多了
浅谈Dijkstra
以下是(可以到处粘贴的)模板:
//stl-priority_queue实现 堆优化dijkstra
#include<cstdio>
#include<algorithm>
#include<iostream>
#include<vector>
#include<queue>
#include<cstring>
#include<ctime>
using namespace std;
#define MAXN 200005
#define MAXM 1005
#define LL long long
#define INF 0x3f3f3f3f
#define inf 0x7fffffff
int n,m,s;
vector<pair<int,LL> >G[MAXN];
LL d[MAXN];
priority_queue< pair<LL,int>, vector< pair<LL,int> >,greater<pair<LL,int> > >Q;
void Init()
{
while(!Q.empty())
Q.pop();
memset(d,0x7f,sizeof(d));
}
void dijkstra()
{
Init();
d[s]=0;Q.push(make_pair(0,s));
while(!Q.empty())
{
pair<LL,int> tmp=Q.top();
Q.pop();
int u=tmp.second;
if(tmp.first>d[u]) continue;
for(int i=0;i<G[u].size();i++)
{
int v=G[u][i].first; LL w=G[u][i].second;
if(d[v]>d[u]+w)
{
d[v]=d[u]+w;
Q.push(make_pair(d[v],v));
}
}
}
}
int main()
{
scanf("%d %d %d",&n,&m,&s);
for(int i=1;i<=m;i++)
{
int u,v; LL w;
scanf("%d %d %lld",&u,&v,&w);
G[u].push_back(make_pair(v,w));
}
dijkstra();
for(int i=1;i<=n;i++)
printf("%lld ",d[i]);
return 0;
}
floyd
这个也比较简单
就不说多了
我只会
n
3
n^3
n3和邻接矩阵那个最最简单的版本
不过出题人一般都比较有良知
没有碰到过被卡的情况 就这样吧
还是甩一篇博客
void floyd()
{
for(int p=1;p<=k;p++)
for(int i=1;i<=k;i++)
for(int j=1;j<=k;j++)
g[i][j]=min(g[i][j],g[i][p]+g[p][j]);
}
spfa
关于spfa:它死了
可是我对spfa 情有独钟情深意切一往情深 敝帚自珍 (想当年我只会spfa 靠着spfa走天下) 所以继续搞它
我以为我把它搞活了:
搞了一个小优化 很多题都可以过(甚至还可以过一道叫做Dijkstra的题目)
但是…正权边的东西还是留给Dij吧
不过负权边的话,好像普通spfa更优秀一点
如果有出题人要卡负权图的spfa的话emmm…(真良心)
还是贴一下代码吧
#include<cstdio>
#include<algorithm>
#include<iostream>
#include<vector>
#include<queue>
#include<cstring>
#include<ctime>
using namespace std;
#define MAXN 2005
#define MAXM 3005
#define LL long long
#define INF 0x3f3f3f3f
int n,m,s;
vector<pair<int,int> >G[MAXN];
int d[MAXN];
struct cmp
{
bool operator()(int a,int b)
{
return d[a]>d[b];
}
};
priority_queue<int,vector<int>,cmp> Q;
//queue<int>Q;
bool f[MAXN],vis[MAXN];
void Init()
{
while(!Q.empty())
Q.pop();
memset(vis,0,sizeof(vis));
for(int i=1;i<=n;i++)
d[i]=1e10;
//memset(d,INF,sizeof(d));
}
void spfa()
{
Init();
Q.push(s);d[s]=0;vis[s]=1;
while(!Q.empty())
{
int u=Q.top();
Q.pop();
vis[u]=0;
for(int i=0;i<G[u].size();i++)
{
int v=G[u][i].first;
LL w=G[u][i].second;
if(d[u]+w<d[v])
{
d[v]=d[u]+w;
if(!vis[v])
{
vis[v]=1;
Q.push(v);
}
}
}
}
}
int main()
{
scanf("%d %d %d",&n,&m,&s);
for(int i=1;i<=m;i++)
{
int u,v,w;
scanf("%d %d %d",&u,&v,&w);
G[u].push_back(make_pair(v,w));
}
spfa();
for(int i=1;i<=n;i++)
printf("%lld ",d[i]);
return 0;
}
判负环:
判负环这种东西还是交给我spfa吧
#include<cstdio>
#include<algorithm>
#include<iostream>
#include<vector>
#include<queue>
#include<cstring>
#include<ctime>
using namespace std;
#define MAXN 2005
#define MAXM 3005
#define LL long long
#define INF 0x3f3f3f3f
int n,m;
vector<pair<int,int> >G[MAXN];
int d[MAXN],len[MAXN];
//事实证明负权图用普通的queue还要跑得好一些
/*struct cmp
{
bool operator()(int a,int b)
{
return d[a]>d[b];
}
};*/
//priority_queue<int,vector<int>,cmp> Q;
queue<int>Q;
bool f[MAXN],vis[MAXN];
void Init()
{
while(!Q.empty())
Q.pop();
memset(vis,0,sizeof(vis));
for(int i=1;i<=n;i++)
d[i]=1e10;
//memset(d,INF,sizeof(d));
}
bool spfa(int s)
{
Init();
Q.push(s);d[s]=0;vis[s]=1;len[s]=0;f[s]=1;
while(!Q.empty())
{
int u=Q.front();
Q.pop();
vis[u]=0;
for(int i=0;i<G[u].size();i++)
{
int v=G[u][i].first;
LL w=G[u][i].second;
if(d[u]+w<d[v])
{
d[v]=d[u]+w;
f[v]=1;
len[v]=len[u]+1;
if(len[v]>=n) return 1;
if(!vis[v])
{
vis[v]=1;
Q.push(v);
}
}
}
}
return 0;
}
int main()
{
int T;
scanf("%d",&T);
while(T--)
{
scanf("%d %d",&n,&m);
for(int i=1;i<=n;i++)
G[i].clear();
for(int i=1;i<=m;i++)
{
int u,v,w;
scanf("%d %d %d",&u,&v,&w);
G[u].push_back(make_pair(v,w));
if(w>=0) G[v].push_back(make_pair(u,w));
}
memset(f,0,sizeof(f));
bool fg=0;
for(int i=1;i<=n;i++)
if(!f[i]&&spfa(i))
{
printf("YE5\n");
fg=1;
break;
}
if(!fg) printf("N0\n");
}
return 0;
}
(以上为判断图中是否存在负环 而甩出来的那道模板题则是判断是否从1开始存在负环 spfa的起点需要调整)
以下是上个暑假留的坑
RT 其实上个暑假就想搞这个来着
但是好像又搁浅了很久(主要是之前的板子一直够用)
最近复习想了想还是重新搞了以下 推陈出新
去年的版本又不想删 其实是很详细的(就是没写完 误 )
想了以下最短路其实掌握上面的那些板子 比赛就其实够用了
写在前面
最短路四个基础算法其实很容易理解,但是懒呐,背不到板,然后又不习惯邻接表【vector其实还好】,然后搁浅了很久。暑假集训还是总结一下吧。
概述
最短路径问题是图论研究中的一个经典算法问题,旨在寻找图中两结点之间的最短路径。算法具体的形式包括:
- 确定起点的最短路径问题 -即已知起始结点,求最短路径的问题。
- 确定终点的最短路径问题 -与确定起点的问题相反,该问题是已知终结结点,求最短路径的问题。在无向图中该问题与确定起点的问题完全等同,在有向图中该问题等同于把所有路径方向反转的确定起点的问题。
- 确定起点终点的最短路径问题 -即已知起点和终点,求两结点之间的最短路径。
- 全局最短路径问题 -求图中所有的最短路径。
Dijkstra算法
一、最短路径的最优子结构性质
该性质描述为:如果P(i,j)={Vi…Vk…Vs…Vj}是从顶点i到j的最短路径,k和s是这条路径上的一个中间顶点,那么P(k,s)必定是从k到s的最短路径。下面证明该性质的正确性。
假设P(i,j)={Vi…Vk…Vs…Vj}是从顶点i到j的最短路径,则有P(i,j)=P(i,k)+P(k,s)+P(s,j)。而P(k,s)不是从k到s的最短距离,那么必定存在另一条从k到s的最短路径P’(k,s),那么P’(i,j)=P(i,k)+P’(k,s)+P(s,j) < P(i,j)。则与P(i,j)是从i到j的最短路径相矛盾。因此该性质得证。
借鉴自:http://www.cnblogs.com/dolphin0520/archive/2011/08/26/2155202.html
二、Dijkstra算法思想
由上述性质可知,如果存在一条从i到j的最短路径(Vi…Vk,Vj),Vk是Vj前面的一顶点。那么(Vi…Vk)也必定是从i到k的最短路径。
Dijkstra算法利用此性质,以最短路径长度递增,逐次生成最短路径。类似贪心的思想,对于一个点,选择与其相邻的所有点中距离最近的点,更新dist[j]=min{dist[j],dist[i]+a[i][j]}
https://blog.csdn.net/qq_35644234/article/details/60870719#t2
https://blog.csdn.net/u013132758/article/details/52293788
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 使用C#创建一个MCP客户端
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现