单源最短路径

先上图
在这里插入图片描述
就记一下SPFA和DIJKSTRA
BELLMANFORD没有了,因为用不到;
SPFA完整代码,包括判负环和求最短路;

SPFA

#include<iostream>
#include<queue>
#include<cstring>
#include<cstdio>
using namespace std;
const int N=1010,M=100010;
int n,m,s,h[N],w[M],e[M],ne[M],idx;
int dist[N],cnt[N];
bool st[N];
void add(int a,int b,int c){
	e[idx]=b,w[idx]=c,ne[idx]=h[a],h[a]=idx++;
}bool judge(){
	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;
}void spfa(){
	dist[s]=0;
	queue<int>q;
	q.push(s);
	st[s]=true;
	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 ;
}
int main()
{
	scanf("%d%d%d",&n,&m,&s);
	int a,b,c;
	memset(h,-1,sizeof h);
	for(int i=1;i<=m;i++){
		scanf("%d%d%d",&a,&b,&c);
		add(a,b,c);
	}
	if(judge()){
		puts("-1");return 0;
	}else{
		memset(st,0,sizeof st);
		memset(dist,0x3f,sizeof dist);
		spfa();
		for(int i=1;i<=n;i++){
			printf("%d\n",dist[i]);
		}
	}
	return 0;
}
原理

SPFA 是BELLMANFORD的队列优化版本,每次都能从队列中取出一个点来然后再用这个点和这个点的三角形不等式来更新这个点周围的点,但是一个点周围的点并不一定都是说通过这个点可以得到最短路的,但是肯定至少有一个点可以用原点来更新,而那个点从此就去更新周围的点;
所以更新到后面的时候会把所有的都更新过去知道找不到更好的;
判负环呢就是说负环好像是一个BUFF,每次转一圈距离都能小一点;如果我转了超过n-1次那么就说明有负环咯;
讲一下优化

SLF

SLF是SPFA的双端队列优化,就是说每次入队的时候跟对头比一下,比队头小插进前面不然就插进后面,为什么能说这样是可以优化 的呢;
来张图;
在这里插入图片描述
图很好看,我不说都知道;
如果用普通的SPFA的话,那么每次都是向外扩展一条边,那么666的也是一直扩展,如果改成双端队列,虽然不是单调的但大概还是单调的,就起到了优先队列的作用;
但是呢优先队列会乘上一个logN,显然1 的比较好;这就是有时候用双端队列的原因,刚开始学的时候不清楚,觉得这东西他吗的没屁用,现在是懂了;
然后呢如果把SPFA换成是优先队列的话,那么借用倞阶指南上的话就是和DIJKSTRA是殊途同归,确实基本上是没有区别了;就是看你用不用VIS数组或者说是ST数组吧;

LLL

虽然没打过但是感觉这个优化比较鸡肋;
因为你要维护平均数烦的一笔;
就是说比队列平均数大的插后面反之插前面;

DIJKSTRA

具体呢可以参见我的搜索汇总那篇博客;
之前CE的码已经改过了,和优先队列BFS真的很像,甚至不能说很像甚至是一模一样;
为了方便我复制我再来一发;

代码
#include<iostream>
#include<cstdio>
#include<queue>
#include<cstring>
using namespace std;
const int N=100010,M=1000010;
int head[N],ver[M],edge[M],ne[M],d[N];
bool vis[N];
int n,m,tot;
priority_queue < pair<int,int > >q;

void add(int x,int y,int z){
    ver[++tot]=y;edge[tot]=z;ne[tot]=head[x];head[x]=tot;
}

void dijkstra() {
    memset(d,0x3f,sizeof d);
    memset(vis,0,sizeof vis);
    d[1]=0;
    q.push({0,1});
    while(q.size()){
        int x = q.top().second; q.pop();
        if(vis[x])continue;
        vis[x]=1 ;
        for(int i=head[x];i;i=ne[i]){
            int y=ver[i],z=edge[i];
            if(d[y] > d[x]+z){
                d[y] = d[x] + z;
                q.push({-d[y],y});
            }
        }
    }
}
int main(){
    cin>>n>>m;
    for(int i=1;i<=m;i++){
        int x,y,z;
        scanf("%d%d%d",&x,&y,&z);
        add(x,y,z);
    }
    dijkstra();
    for(int i=1;i<=n;i++){
        printf("%d\n",d[i]);
    }
    return 0;
}

大概就是这样的;
原理和优先队列BFS其实是一样的;
证明不写了,网上搜优先队列证明就行了;
这篇文章后面还要更新几个题目,只不过还有更古老的坑没有填完先去填坑了;

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值