最短路径问题

最短路径的定义

最短路径问题是给定两个顶点,在以这两个点为起点和终点的路径中,边权和最小的路径。如果把权值当作距离,考虑最短距离就好理解了。

单源最短路问题是固定一个起点,求它到其他所有点的最短路问题。终点也固定的问题叫做两点之间最短路径问题。但其复杂度与与单源路问题一样,因此我们也看作单源最短路问题求解。

负权与负环:如果说有一个环他们的权之和小于0就是负环

单源最短路问题1(Bellman-Ford算法)

Bellman-Ford的改进是SPFA算法
题目:设有n个点m条边,每条边的起始点是u终点是v,边权w。
思路:我们设一个dis[]数组,表示固定点到所有点的距离。
初始化
我们遍历每一条边,如果说dis[v]+w<dis[u],那么dis[u]=dis[v]+w(松弛操作)

struct edge{
int from,to,cost
};
edge es[maxn];
int d[maxn];
int V,E;
void shortest_path(int s){
	for(int i=0;i<V;i++)d[i]=INF;
	d[s]=0;
	while(true){
		bool update=false;
		for(int i=0;i<E;i++){
			edge e=es[i];
			if(d[e.from]!=INF&&d[e.to]>d[e.from]+e.cost){
				d[e.to]=d[e.from]+e.cost;
				update=true;
			}
		}
		if(!update)break;
	}
}

单源最短问题2(Dijkstra算法)

如果题目中没有负边权,那么就用Dijkstra算法
Dijkstra算法不断选择全局最小值进行标记和拓展,最终可以得到起点到每个节点的最短路径的长度

P4779 【模板】单源最短路径(标准版)

#include<bits/stdc++.h>
using namespace std;
const int maxn=1e5+10;
const int INF=2147483647;
int n,m,s,dis[maxn],vis[maxn];
struct node{
	int to,w;
}edge;
vector<node>e[maxn];
priority_queue<pair<int,int> >q;
void init(){
	for(int i=1;i<=n;i++){
		e[i].clear();
		dis[i]=INF;
	}
}
void Dijkstra(){
	dis[s]=0;
	q.push(make_pair(-dis[s],s));
	while(!q.empty()){
		int now=q.top().second;
		q.pop();
		if(vis[now])
		continue;
		vis[now]=1;
		for(int i=0;i<e[now].size();i++){
			int to=e[now][i].to;
			if(dis[to]>dis[now]+e[now][i].w){
				dis[to]=dis[now]+e[now][i].w;
				q.push(make_pair(-dis[to],to));
			}
		}
	}
}
int main(){
	scanf("%d%d%d",&n,&m,&s);
	init();
	for(int i=1;i<=m;i++){
		int x,y,z;
		scanf("%d%d%d",&x,&y,&z);
		e[x].push_back((node){y,z});
	}
	Dijkstra();
	for(int i=1;i<=n;i++)
	cout<<dis[i]<<" ";
}
#include<bits/stdc++.h>
using namespace std;
const int maxn=1e5+10;
const int INF=2147483647;
int n,m,s,dis[maxn],vis[maxn];
vector<pair<int,int> >e[maxn];
priority_queue<pair<int,int> >q;
void init(){
	for(int i=1;i<=n;i++){
		e[i].clear();
		dis[i]=INF;
	}
}
void Dijkstra(){
	dis[s]=0;
	q.push(make_pair(-dis[s],s));
	while(!q.empty()){
		int now=q.top().second;
		q.pop();
		if(vis[now])
		continue;
		vis[now]=1;
		for(int i=0;i<e[now].size();i++){
			int to=e[now][i].first;
			if(dis[to]>dis[now]+e[now][i].second){
				dis[to]=dis[now]+e[now][i].second;
				q.push(make_pair(-dis[to],to));
			}
		}
	}
}
int main(){
	scanf("%d%d%d",&n,&m,&s);
	init();
	for(int i=1;i<=m;i++){
		int x,y,z;
		scanf("%d%d%d",&x,&y,&z);
		e[x].push_back(make_pair(y,z));
	}
	Dijkstra();
	for(int i=1;i<=n;i++)
	cout<<dis[i]<<" ";
}

单源最短问题3(SPFA算法)

Bellman-Ford的改进是SPFA算法,Ford每次都把所有的边遍历一遍,其实是一种浪费。我们只需要把一个顶点的dis[]改变的情况下,进行松弛操作即可。
慎用,在毒瘤数据中可退化到O(nm)。
思路:我们用到STL队列,用数组dis记录最短路径,用邻接表存图,用vis数组记录该节点是否在队列中。
步骤:用队列来存储待优化的节点(类似BFS),优化时每次取出队首节点,并且用队首节点来作为最短路径进行更新并进行松弛操作
如果需要对最短路进行更新,且该点不在队列中,我们把它加入到队列中
不断进行松弛操作,直至队列为空。

P3371 【模板】单源最短路径(弱化版)

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=5e5+10;
const int INF=2147483647;
int n,m,s,vis[maxn],dis[maxn];
struct node{
	int to,w;
}p;
vector<node>e[maxn];
void init(){
	for(int i=1;i<=n;i++){
		e[i].clear();
		dis[i]=INF;
		vis[i]=0;
	}
}
void spfa(){
	queue<int> q;
	q.push(s);dis[s]=0;vis[s]=1;
	while(!q.empty()){
		int now=q.front(); vis[now]=1;
		for(int i=0;i<e[now].size();i++){
			int to=e[now][i].to;
			if(dis[to]>dis[now]+e[now][i].w){
			dis[to]=dis[now]+e[now][i].w;
			if(!vis[to]){
			q.push(to);
			vis[to]=1;
			}		
			}
		}
		q.pop();
		vis[now]=0;
	}
}
int main(){
	scanf("%d%d%d",&n,&m,&s);
	init();
	while(m--){
		int x,y,z;
		scanf("%d%d%d",&x,&y,&z);
		p.to=y;p.w=z;
		e[x].push_back(p);
	}
	spfa();
	for(int i=1;i<=n;i++)
	printf("%d ",dis[i]);
	cout<<endl;
}
#include<bits/stdc++.h>
using namespace std;
const int maxn=5e5+10;
const int INF=2147483647;
int n,m,s,vis[maxn],dis[maxn];
vector<pair<int,int> >e[maxn];//tu
queue<int> q;
void init(){
	for(int i=1;i<=n;i++){
		e[i].clear();
		vis[i]=0;
		dis[i]=INF;
	}
}
void spfa(){
	q.push(s);vis[s]=1;dis[s]=0;
	while(!q.empty()){
		int now=q.front();
		q.pop();vis[now]=1;
		for(int i=0;i<e[now].size();i++){
			int to=e[now][i].first;
			if(dis[to]>dis[now]+e[now][i].second){
				dis[to]=dis[now]+e[now][i].second;
				if(!vis[to]){
					vis[to]=1;
					q.push(to);
				}
			}
		}
		vis[now]=0;
	}
}
int main(){
	scanf("%d%d%d",&n,&m,&s);
	init();
	for(int i=1;i<=m;i++){
		int x,y,z;
		scanf("%d%d%d",&x,&y,&z);
		e[x].push_back(make_pair(y,z));
	}
	spfa();
	for(int i=1;i<=n;i++)
	cout<<dis[i]<<" ";
}

任意两点间的最短路问题(Floyd算法)

求解带权图的多源最短路问题,算法原理是动态规划,复杂度O(n^3),不能有负环。
我们首先问从i号顶点到j号顶点的距离,是否可以让i到1再到j的距离代替直接从i到j呢。e[i][1]+e[1][j]<e[i][j].那么i、j经过2顶点呢,三顶点呢

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]);
	}
}

P2910 [USACO08OPEN]Clear And Present Danger S

#include<bits/stdc++.h>
using namespace std;
const int maxn=110;
const int maxn1=1e4+10;
int n,m,sum;
int order[maxn1],dis[maxn][maxn];
void floyd(){
	for(int k=1;k<=n;k++){
		for(int i=1;i<=n;i++){
			for(int j=1;j<=n;j++){
				dis[i][j]=min(dis[i][j],dis[i][k]+dis[k][j]);
			}
		}
	}
}
int main(){
	scanf("%d%d",&n,&m);
	for(int i=1;i<=m;i++)
	scanf("%d",&order[i]);
	for(int i=1;i<=n;i++)
	for(int j=1;j<=n;j++)
	scanf("%d",&dis[i][j]);
	floyd();
	int pre=1;
	for(int i=1;i<=m;i++){
		sum+=dis[pre][order[i]];
		pre=order[i];
	}
	sum+=dis[pre][n];
	cout<<sum<<endl;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值