最短路专题 bellman-ford floyd spfa dijk johnson 个人记录板子

bellman-ford
可以求单源最短路 可以求边数限制的最短路
假设有n个点 m个边
求单源最短路 一共松弛n-1次 因为如果不存在换1到n的最短有n-1条边
每次把所有的边松弛一遍 每次松弛玩我们要把松弛玩的最短路数组备份下来,如果不备份用当前这个来更新,会出现错误
模板题

#include"bits/stdc++.h"
using namespace std;
struct node{
	int u,v,w;
};
node v[50005];
int dis[505],a[505];
const int INF=0x3f3f3f3f;
int n,m,k;
void blm(){
	memset(dis,INF,sizeof(dis));
	dis[1]=0;
	for(int i=1;i<=k;i++){
		memcpy(a,dis,sizeof(dis));//备份 
		for(int j=1;j<=m;j++){
			int x,y,z;
			x=v[j].u,y=v[j].v,z=v[j].w;
			dis[y]=min(dis[y],a[x]+z);//如果不备份在写成这样dis[y]=min(dis[y],dis[x]+z);会出错 
		}
	}
}
int main(){
	cin>>n>>m>>k;
	for(int i=1;i<=m;i++){
		int a,b,c;
		cin>>a>>b>>c;
		v[i].u=a;
		v[i].v=b;
		v[i].w=c;
	}
	blm();
	if(dis[n]>INF/2){//不知道为啥这样 判断 记下来吧 
		cout<<"impossible";
	}
	else{
		cout<<dis[n];
	}
	return 0;
}

floyd
可以求多源最短路
把每个点当成中转点来更新其他点对之间的最短距离

void floyd()
{
    for(int k=1;k<=n;k++)
    {
        for(int i=1;i<=n;i++)
        {
            for(int j=1;j<=n;j++)
            {
                if(dis[i][k]+dis[k][j]<=dis[i][j])
                {
                    dis[i][j]=dis[i][k]+dis[k][j];//当i,j的原来的边的最短距离,大于经过k点所到达的距离那么就替换。
                   
                }
            }
        }
    }
}

spfa
bellman-ford的队列优化
模板题(这题需要sle的spfa优化)
spfa有两种优化 sle LLL优化
普通spfa

#include"bits/stdc++.h"
using namespace std;
struct node{
	int v,w;
};
vector<node>v[30005];
int dis[30005],vis[30005];
int n,r,p,s;
const int INF=0x3f3f3f3f;
void spfa(int x){
	dis[x]=0;
	vis[x]=1;
	queue<int>q;
	q.push(x);
	while(!q.empty()){
		int top=q.front();
		q.pop();
		vis[top]=0;
		for(int i=0;i<v[top].size();i++){
			int dv=v[top][i].v;
			int dw=v[top][i].w;
			if(dis[dv]>dis[top]+dw){
				dis[dv]=dis[top]+dw;
				if(vis[dv]==0){
					q.push(dv);
					vis[dv]=1;
				}
			}
			
		} 
	}
}
int main(){
	cin>>n>>r>>p>>s;
	for(int i=1;i<=r;i++){
		int a,b,c;
		cin>>a>>b>>c;
		v[a].push_back(node{b,c});
		v[b].push_back(node{a,c});
	}
	for(int i=1;i<=p;i++){
		int a,b,c;
		cin>>a>>b>>c;
		v[a].push_back(node{b,c});
	}
	memset(dis,INF,sizeof(dis));
	spfa(s);
	for(int i=1;i<=n;i++){
		if(dis[i]==INF){
			cout<<"NO PATH"<<endl;
		}
		else{
			cout<<dis[i]<<endl;
		}
	}
	return 0;
}

sle优化
Small Label First 策略,设要加入的节点是j,队首元素为i,若dist(j)<dist(i),则将j插入队首,否则插入队尾。

#include"bits/stdc++.h"
using namespace std;
struct node{
	int v,w;
};
vector<node>v[30005];
int dis[30005],vis[30005];
int n,r,p,s;
const int INF=0x3f3f3f3f;
void spfa(int x){
	dis[x]=0;
	vis[x]=1;
	deque<int>q;
	q.push_back(x);
	while(!q.empty()){
		int top=q.front();
		q.pop_front();
		vis[top]=0;
		for(int i=0;i<v[top].size();i++){
			int dv=v[top][i].v;
			int dw=v[top][i].w;
			if(dis[dv]>dis[top]+dw){
				dis[dv]=dis[top]+dw;
				if(vis[dv]==0){
					if(dis[dv]<dis[q.front()])q.push_front(dv);
					else q.push_back(dv);
					vis[dv]=1;
				}
			}
		} 
	}
}
int main(){
	cin>>n>>r>>p>>s;
	for(int i=1;i<=r;i++){
		int a,b,c;
		cin>>a>>b>>c;
		v[a].push_back(node{b,c});
		v[b].push_back(node{a,c});
	}
	for(int i=1;i<=p;i++){
		int a,b,c;
		cin>>a>>b>>c;
		v[a].push_back(node{b,c});
	}
	memset(dis,INF,sizeof(dis));
	spfa(s);
	for(int i=1;i<=n;i++){
		if(dis[i]==INF){
			cout<<"NO PATH"<<endl;
		}
		else{
			cout<<dis[i]<<endl;
		}
	}
	return 0;
}

dijk
dijk有朴素版本 我写堆优化
模板题

#include"bits/stdc++.h"
using namespace std;
typedef long long ll;
struct node{
	ll v,w;
};
vector<node>v[200000];
struct edge{
	ll id,num;
	friend bool operator <(edge a,edge b){
		return a.num>b.num;
	}
};
ll n,m,s;
ll visit[200000],dis[200000];
void dijk(ll x){
	for(int i=1;i<=n;i++)dis[i]=1e15,visit[i]=0;
	priority_queue<edge>q;
	dis[x]=0;
	q.push(edge{x,0});
	while(!q.empty()){
		edge top=q.top();
		q.pop();
		if(visit[top.id])continue;
		visit[top.id]=1;
		for(int i=0;i<v[top.id].size();i++){
			ll dv=v[top.id][i].v;
			ll dw=v[top.id][i].w;
			if(dis[dv]>dis[top.id]+dw){
				dis[dv]=dis[top.id]+dw;
				q.push(edge{dv,dis[dv]});
			}
		}
	}
}
int main(){
	cin>>n>>m>>s;
	for(int i=1;i<=m;i++){
		ll x,y,z;
		cin>>x>>y>>z;
		v[x].push_back(node{y,z});
	}
	dijk(s);
	for(int i=1;i<=n;i++)cout<<dis[i]<<" ";
	return 0;
}

johnson是求全源最短路,floyd也可以 但是复杂度O(n3)太高了
可以处理有负边的
首先我们要处理负边v[i].w+=h[i]-h[v[i].v];
这个h表示从超级源点到每个节点的最短路长度这样我就可以以使每个边为正 然后跑n边dijk即可最后最短路径减去加的值
模板题

#include"bits/stdc++.h"
using namespace std;
typedef long long ll;
struct node{
	ll v,w;
};
struct edge{
	ll id,num;
	friend bool operator<(edge a,edge b){
		return a.num>b.num;
	}
};
vector<node>v[30005];
ll visit[30005],dis[30005],cnt[30005],h[30005];
ll n,m;
priority_queue<edge>q;
bool spfa(int x){
	h[x]=0;visit[x]=1;
	queue<ll>q;
	q.push(x);
	while(!q.empty()){
		int top=q.front();
		q.pop();
		visit[top]=0;
		for(int i=0;i<v[top].size();i++){
			ll dv=v[top][i].v;
			ll dw=v[top][i].w;
			if(h[dv]>h[top]+dw){
				h[dv]=h[top]+dw;
				q.push(dv);
				if(visit[dv]==0)visit[dv]=1;
				cnt[dv]++;
				if(cnt[dv]>=n)return true;
			}
		}
	}
	return false;
}
void dijk(int x){
	dis[x]=0;
	q.push(edge{x,0});
	while(!q.empty()){
		edge top=q.top();
		q.pop();
		if(visit[top.id])continue;
		visit[top.id]=1;
		for(int i=0;i<v[top.id].size();i++){
			ll dv=v[top.id][i].v;
			ll dw=v[top.id][i].w;
			if(dis[dv]>dis[top.id]+dw){
				dis[dv]=dis[top.id]+dw;
				q.push(edge{dv,dis[dv]});
			}
		}
	}
}
int main(){
	scanf("%lld %lld",&n,&m);
	for(int i=1;i<=m;i++){
		ll a,b,c;
		scanf("%lld %lld %lld",&a,&b,&c);
		v[a].push_back(node{b,c});
	}
	for(int i=1;i<=n;i++){
		v[0].push_back(node{i,0});
	}
	memset(h,0x3f3f3f3f,sizeof(h));
	if(spfa(0)){
		cout<<"-1";
		return 0;
	}
	for(int i=1;i<=n;i++){
		for(int j=0;j<v[i].size();j++){
			v[i][j].w+=h[i]-h[v[i][j].v];
		} 
	}
	for(int i=1;i<=n;i++){
		memset(visit,0,sizeof(visit));
		memset(dis,0x3f3f3f3f,sizeof(dis));
		dijk(i);
		ll ans=0;
		for(int j=1;j<=n;j++){
			if(i==j)continue;
			if(dis[j]>=1e15)ans+=1LL*j*1e9;
			else ans+=1LL*(dis[j]+h[j]-h[i])*j;
		}
		printf("%lld\n",ans);
	}
	return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值