迪杰斯特拉和spfa
迪杰斯特拉
Dijkstra算法是典型的算法。Dijkstra算法是很有代表性的算法。Dijkstra一般的表述通常有两种方式,一种用永久和临时标号方式,一种是用OPEN, CLOSE表的方式,这里均采用永久和临时标号的方式。注意该算法要求图中不存在负权边。
基本定义
Dijkstra(迪杰斯特拉)算法是典型的单源最短路径算法,用于计算一个节点到其他所有节点的最短路径。主要特点是以起始点为中心向外层层扩展,直到扩展到终点为止。Dijkstra算法是很有代表性的最短路径算法,在很多专业课程中都作为基本内容有详细的介绍,如数据结构,图论,运筹学等等。Dijkstra一般的表述通常有两种方式,一种用永久和临时标号方式,一种是用OPEN, CLOSE表的方式,这里均采用永久和临时标号的方式。注意该算法要求图中不存在负权边。
相关原理
首先,引进一个辅助向量D,它的每个分量D表示当前所找到的从始点v到每个终点vi的最短路径的长度。如D[3]=2表示从始点v到终点3的路径相对最小长度为2。这里强调相对就是说在算法过程中D的值是在不断逼近最终结果但在过程中不一定就等于最短路径长度。它的初始状态为:若从v到vi有弧,则D为弧上的权值;否则置D为∞。显然,长度为 D[j]=Min{D | vi∈V} 的路径就是从v出发的长度最短的一条最短路径。此路径为(v,vj)。 那么,下一条长度次短的最短路径是哪一条呢?假设该次短路径的终点是vk,则可想而知,这条路径或者是(v,vk),或者是(v,vj,vk)。它的长度或者是从v到vk的弧上的权值,或者是D[j]和从vj到vk的弧上的权值之和。 一般情况下,假设S为已求得最短路径的终点的集合,则可证明:下一条最短路径(设其终点为X)或者是弧(v,x),或者是中间只经过S中的顶点而最后到达顶点X的路径。因此,下一条长度次短的最短路径的长度必是D[j]=Min{D | vi∈V-S} 其中,D或者是弧(v,vi)上的权值,或者是Dk和弧(vk,vi)上的权值之和。迪杰斯特拉算法描述如下: 1)arcs表示弧上的权值。若不存在,则置arcs为∞(在本程序中为MAXCOST)。S为已找到从v出发的最短路径的终点的集合,初始状态为空集。那么,从v出发到图上其余各顶点vi可能达到的最短路径长度的初值为D=arcs[Locate Vex(G,v),i] vi∈V 2)选择vj,使得D[j]=Min{D | vi∈V-S} 3)修改从v出发到集合V-S上任一顶点vk可达的最短路径长度。
普通迪杰斯特拉模板
时间复杂是 O(n2+m)O(n2+m), nn 表示点数,mm 表示边数
int g[N][N]; // 存储每条边
int dist[N]; // 存储1号点到每个点的最短距离
bool st[N]; // 存储每个点的最短路是否已经确定
// 求1号点到n号点的最短路,如果不存在则返回-1
int dijkstra()
{
memset(dist, 0x3f, sizeof dist);
dist[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 || dist[t] > dist[j]))
t = j;
// 用t更新其他点的距离
for (int j = 1; j <= n; j ++ )
dist[j] = min(dist[j], dist[t] + g[t][j]);
st[t] = true;
}
if (dist[n] == 0x3f3f3f3f) return -1;
return dist[n];
}
自己的
#include<bits/stdc++.h>
using namespace std;
struct node{
int dian;
int bian;
}sss;
int n,m,s;
vector<node>adj[100001];
int d[100001];
bool dd[100001]={false};
void dijisite(int s){
fill(d,d+n+1,0x7fffffff);
d[s]=0;
for(int i=1;i<=n;i++){
int u=-1,minn=999999999;
for(int j=1;j<=n;j++){
if(dd[j]==false&&d[j]<minn){
u=j;
minn=d[j];
}
}
if(u==-1){
return ;
}
dd[u]=true;
for(int j=0;j<adj[u].size();j++){
int dian=adj[u][j].dian;
if(dd[dian]==false){
d[dian]=min(d[dian],d[u]+adj[u][j].bian);
}
}
}
}
int main(){
cin>>n>>m>>s;
for(int i=1;i<=m;i++){
int x,y,z;
cin>>x>>y>>z;
sss.dian=y;
sss.bian=z;
adj[x].push_back(sss);
}
dijisite(s);
for(int i=1;i<=n;i++){
cout<<d[i]<<" ";
}
return 0;
}
堆优化的迪杰斯特拉模板
时间复杂度 O(mlogn)O(mlogn), nn 表示点数,mm 表示边数
typedef pair<int, int> PII; int n; // 点的数量
int h[N], w[N], e[N], ne[N], idx; // 邻接表存储所有边
int dist[N]; // 存储所有点到1号点的距离
bool st[N]; // 存储每个点的最短距离是否已确定 // 求1号点到n号点的最短距离,如果不存在,则返回-1
int dijkstra()
{
memset(dist, 0x3f, sizeof dist);
dist[1] = 0;
priority_queue<PII, vector<PII>, greater<PII>> heap;
heap.push({0, 1}); // first存储距离,second存储节点编号 while (heap.size())
{
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] > distance + w[i])
{
dist[j] = distance + w[i];
heap.push({dist[j], j});
}
}
} if (dist[n] == 0x3f3f3f3f) return -1;
return dist[n];
}
自己的代码
#include<bits/stdc++.h>
using namespace std;
long long n,m,s;
struct node{
long long to;
long long w;
inline bool operator < (const node &b)const{
return w>b.w;
}
}sss;
vector<node>adj[1000005];
priority_queue<node>q;
long long d[1000005];
bool dd[1000005]={false};
int main(){
cin>>n>>m>>s;
memset(d,127,sizeof(d));
for(long long i=1;i<=m;i++){
long long x,y,z;
cin>>x>>y>>z;
sss.to=y;
sss.w=z;
adj[x].push_back(sss);
}
d[s]=0;
q.push((node){s,0});
while(q.empty()!=true){
int x=q.top().to;
q.pop();
if(dd[x]==true){
continue;
}
dd[x]=true;
for(long long i=0;i<adj[x].size();i++){
int nx=adj[x][i].to,w=adj[x][i].w;
if(d[nx]>w+d[x]){
d[nx]=w+d[x];
q.push((node){nx,d[nx]});
}
}
}
for(long long i=1;i<=n;i++){
cout<<d[i]<<" ";
}
return 0;
}
SPFA 算法
SPFA 算法是 Bellman-Ford算法 的队列优化算法的别称,通常用于求含负权边的单源最短路径,以及判负权环。SPFA 最坏情况下复杂度和朴素 Bellman-Ford 相同,为 O(VE)。
基本信息
中文名称
贝尔曼-福特算法的队列优化形式
外文名称 Bellman-Ford using queue optimization
简称
SPFA
全称
Shortest Path Faster Algorithm
用途
若给定的图存在负权边,类似Dijkstra算法等算法便没有了用武之地,SPFA算法便派上用场了。简洁起见,我们约定加权有向图G不存在负权回路,即最短路径一定存在。用数组d记录每个结点的最短路径估计值,而且用邻接表来存储图G。我们采取的方法是动态逼近法:设立一个先进先出的队列用来保存待优化的结点,优化时每次取出队首结点u,并且用u点当前的最短路径估计值对离开u点所指向的结点v进行松弛操作,如果v点的最短路径估计值有所调整,且v点不在当前的队列中,就将v点放入队尾。这样不断从队列中取出结点来进行松弛操作,直至队列空为止。
为了避免最坏情况的出现,在正权图上应使用效率更高的Dijkstra算法。
定理:
只要最短路径存在,上述SPFA算法必定能求出最小值。证明:每次将点放入队尾,都是经过松弛操作达到的。换言之,每次的优化将会有某个点v的最短路径估计值d[v]变小。所以算法的执行会使d越来越小。由于我们假定图中不存在负权回路,所以每个结点都有最短路径值。因此,算法不会无限执行下去,随着d值的逐渐变小,直到到达最短路径值时,算法结束,这时的最短路径估计值就是对应结点的最短路径值。
实际上,如果一个点进入队列达到n次,则表明图中存在负环,没有最短路径。
对SPFA的一个很直观的理解就是由无权图的BFS转化而来。在无权图中,BFS首先到达的顶点所经历的路径一定是最短路(也就是经过的最少顶点数),所以此时利用数组记录节点访问可以使每个顶点只进队一次,但在带权图中,最先到达的顶点所计算出来的路径不一定是最短路。一个解决方法是放弃数组,此时所需时间自然就是指数级的,所以我们不能放弃数组,而是在处理一个已经在队列中且当前所得的路径比原来更好的顶点时,直接更新最优解。
SPFA算法有两个优化策略SLF和LLL--SLF:Small Label First 策略,设要加入的节点是j,队首元素为i,若dist(j)<dist(i),则将j插入队首,否则插入队尾; LLL:Large Label Last 策略,设队首元素为i,队列中所有dist值的平均值为x,若dist(i)>x则将i插入到队尾,查找下一元素,直到找到某一i使得dist(i)<=x,则将i出队进行松弛操作。SLF 和 LLF 在随机数据上表现优秀,但是在正权图上最坏情况为 O(VE),在负权图上最坏情况为达到指数级复杂度。
模板
spfa 算法(队列优化的Bellman-Ford算法)
时间复杂度 平均情况下 O(m)O(m),最坏情况下 O(nm)O(nm), nn 表示点数,mm 表示边数
int n; // 总点数
int h[N], w[N], e[N], ne[N], idx; // 邻接表存储所有边
int dist[N]; // 存储每个点到1号点的最短距离
bool st[N]; // 存储每个点是否在队列中 // 求1号点到n号点的最短路距离,如果从1号点无法走到n号点则返回-1
int spfa()
{
memset(dist, 0x3f, sizeof dist);
dist[1] = 0; queue<int> q;
q.push(1);
st[1] = true; while (q.size())
{
auto 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]) // 如果队列中已存在j,则不需要将j重复插入
{
q.push(j);
st[j] = true;
}
}
}
} if (dist[n] == 0x3f3f3f3f) return -1;
return dist[n];
}
自己的代码
#include<bits/stdc++.h>
using namespace std;
struct node{
int to;
int w;
}sss;
vector<node>adj[1000001];
queue<int>q;
bool visit[1000001];
int n,m,s;
long long cost[1000001];
void spfa(){
memset(visit,false,sizeof(visit));
q.push(s);
visit[s]=true;
cost[s]=0;
while(q.empty()!=true){
int t=q.front();
visit[t]=true;
for(int i=0;i<adj[t].size();i++){
int to=adj[t][i].to;
if(cost[to]>cost[t]+adj[t][i].w){
cost[to]=cost[t]+adj[t][i].w;
if(visit[to]==false){
visit[to]=true;
q.push(to);
}
}
}
visit[t]=false;
q.pop();
}
}
int main(){
cin>>n>>m>>s;
for(int i=1;i<=m;i++){
int a,b,c;
cin>>a>>b>>c;
sss.to=b;
sss.w=c;
adj[a].push_back(sss);
cost[i]=0x7fffffff;
}
spfa();
for(int i=1;i<=n;i++){
cout<<cost[i]<<" ";
}
return 0;
}
迪杰斯特拉和spfa的更多相关文章
- hdu 1874 畅通工程续(迪杰斯特拉优先队列,floyd,spfa)
畅通工程续 Time Limit: 3000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others) Total Subm ...
- 写一下SPFA和迪杰斯特拉的模版。。。第一次写博客,有错请提出哦!
SPFA的模版 #include<bits/stdc++.h> using namespace std; queue <int> q; typedef pair <int ...
- 图的最短路径---迪杰斯特拉(Dijkstra)算法浅析
什么是最短路径 在网图和非网图中,最短路径的含义是不一样的.对于非网图没有边上的权值,所谓的最短路径,其实就是指两顶点之间经过的边数最少的路径. 对于网图,最短路径就是指两顶点之间经过的边上权值之和最 ...
- C#迪杰斯特拉算法
C#迪杰斯特拉算法 网上有许多版本的,自己还是写一个理解点 Dijkstra.cs public class Dijkstra { private List<Node> _nodes; p ...
- C++迪杰斯特拉算法求最短路径
一:算法历史 迪杰斯特拉算法是由荷兰计算机科学家狄克斯特拉于1959 年提出的,因此又叫狄克斯特拉算法.是从一个顶点到其余各顶点的最短路径算法,解决的是有向图中最短路径问题.迪杰斯特拉算法主要特点是以 ...
- 【算法杂谈】LJX的迪杰斯特拉算法报告
迪杰斯特拉(di jie qi)算法 这里有一张图: 假设要求从1号节点到5号节点的最短路.那么根据迪杰斯特拉算法的思想,我们先看: 节点1,从节点1出发的一共有3条路,分别是1-6.1-3.1-2. ...
- C# 迪杰斯特拉算法 Dijkstra
什么也不想说,现在直接上封装的方法: using System; using System.Collections.Concurrent; using System.Collections.Gener ...
- 迪杰斯特拉(dijkstra)算法的简要理解和c语言实现(源码)
迪杰斯特拉(dijkstra)算法:求最短路径的算法,数据结构课程中学习的内容. 1 . 理解 算法思想::设G=(V,E)是一个带权有向图,把图中顶点集合V分成两组,第一组为已求出最短路径的顶点集合 ...
- 图-最短路径-Dijktra(迪杰斯特拉)算法
1. 迪杰斯特拉算法是由荷兰计算机科学家狄克斯特拉算法于1959 年提出的,因此又叫狄克斯特拉算法.是从一个顶点到其余各顶点的最短路径算法,解决的是有向图中最短路径问题.迪杰斯特拉算法主要特点是以起始 ...
随机推荐
- 我是怎样刚拿饿了么P7 offer,然后途虎一轮游的
今年初拿了个饿了么P7的offer,于此同时大家顺便看看我怎么途虎一轮游的.废话不多说,直接上题吧. 一面 首先上来就是自我介绍,简单的说下自己的项目经验,涉及的技术栈之类的. 然后每一轮必问的问题来 ...
- js实现csv下载
var TableDatas = '数据源';function getCSV(){ if(this.TableDatas.length === 0){ alert("没有数据呀呀呀呀!&q ...
- Android开发之将图片文件转化为字节数组字符串,并对其进行Base64编码处理
作者:程序员小冰,CSDN博客:http://blog.csdn.net/qq_21376985, 转载请说明出处. /** * @将图片文件转化为字节数组字符串,并对其进行Base64编码处理 * ...
- 2020最新Servlet+form表单实现文件上传(图片)
servlet实现文件上传接受 这几天学了一点文件上传,有很多不会,在网查了许多博客,但是最新的没有,都比较久了 因为我是小白,版本更新了,以前的方法自己费了好久才弄懂,写个随笔方便以后查找 代码奉上 ...
- LightOJ - 1214-Large Division(c++取模 + java的两种写法)
Given two integers, a and b, you should check whether a is divisible by b or not. We know that an in ...
- 深入了解Kafka【一】概述与基础架构
1.概述 Kafka是一个分布式的.基于发布订阅的消息系统,主要解决应用解耦.异步消息.流量削峰等问题. 2.发布订阅模型 消息生产者将消息发布到Topic中,同时有多个消息消费者订阅该消息,消费者消 ...
- vue实现局部预览打印
html <div id="subOutputRank-print"> <h1></h1> </div> js doPrint(e) ...
- 教你用OpenCV 和 Python给证件照换底色(蓝底 <->红底->白底)
在我们的生活中常常要用到各种底色要求的证件电子照,红底.蓝底.或者白底,而假如你手上只有一种底色的证件照,你又不想再去拍又不会PS怎么办?今天教你们用OpenCV和Python给你的证件照换底色. P ...
- JVM学习第三天(JVM的执行子系统)之开篇Class类文件结构
虽然这几天 很忙,但是学习是不能落下的,也不能推迟,因为如果推迟了一次,那么就会有无数次;加油,come on! Java跨平台的基础: 各种不同平台的虚拟机与所有平台都统一使用的程序存储格式——字节 ...
- Spring Boot入门,源码解析
目录 1.Spring Boot简介 2.微服务 3.Spring Boot HelloWorld 3.1 创建一个Maven工程 3.2 导入依赖Spring Boot相关的依赖 3.3 编写一个主 ...