最短路学习笔记

三种算法
floyd
复杂度 :O(n^3)
应用场景:多源最短路,非负边
邻接矩阵存图

最短路

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int INF = 0x3f3f3f3f;
int n,m;
const int MAX_N = 105;
int d[MAX_N][MAX_N];
int main() {
	while(scanf("%d%d",&n,&m),n||m){
		memset(d,0x3f,sizeof(d));
		
		for(int i = 1;i <= n;i++)	d[i][i] = 0;
		for(int i = 1;i <= m;i++){
			int u,v,x;
			scanf("%d%d%d",&u,&v,&x);
			d[u][v] = min(d[u][v],x);
			d[v][u] = min(d[v][u],x);
		}
		
		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]);
				}
			}
		}
		printf("%d\n",d[1][n]);
	}

	return 0;
}

dijkstra:
复杂度O(n^2),优先队列优化后O((n+m)logn)
应用场景:无负边
链式前向星存图
Til the Cows Come Home

#include <cstdio>
#include <queue>
#include <cstring>
#define min(x,y) x<y?x:y
#define P pair<int,int> 
using namespace std;
const int INF = 0x3f3f3f3f;
int n,m;
int tot = 0;
const int MAX_N = 1e3+5;
const int MAX_M = 2e3+10;
int d[MAX_N];
int edge[MAX_M<<1];
int ver[MAX_M<<1];
int Next[MAX_M<<1];
int head[MAX_M<<1];
void add(int x,int y,int z){
	ver[tot] = y;
	edge[tot] = z;
	Next[tot] = head[x];
	head[x] = tot++;
}
void init(){
	for(int i=1;i <= MAX_M;i++){
		head[i] = Next[i] = -1;
		
	}
	memset(d,0x3f,sizeof(d));
}
int main() {
	scanf("%d%d",&m,&n);
	init();
	for(int i = 1;i <= m;i++){
		int x,y,z;
		scanf("%d%d%d",&x,&y,&z);
		add(x,y,z);
		add(y,x,z);
	}
	
	priority_queue< P > q;//优先队列以pair第一维大到小排序,由最短路(最小值),所以放入(-d[i])
	d[n] = 0;
	q.push(P(0,n));
	while(q.size()){
		P p = q.top(); 	q.pop();
		int u = p.second;
		if(-p.first > d[u])continue;
		for(int i = head[u];~i;i = Next[i]){
			int v = ver[i],dis = edge[i];
			if(d[v] > d[u] + dis){
				d[v] = d[u] + dis;
				q.push(P(-d[v],v));
			}
		}
	}
	printf("%d",d[1]);
	return 0;
}

其中(-p.first > d[u])有另一种方式
用vis数组

if(vis[u])continue;
else {
	vis[u] = 1;
	...
	...
}

2021.7.13更新
今天学了Bellman-ford算法, 原因在于这道题
Currency exchange
题意是:有n种货币,给你一些某种货币, 你要不断的换钱,使自己的钱增加。而换币会有汇率和服务费。容易发现, 如果存在一个环, 通过不断地换币回到同一种货币, 若钱的数量增多, 则存在bug可以无限刷钱,输出Yes, 不然输出No
知识点:Bellman-ford算法和最长路
先讲Bellman-ford(没有队列优化)
代码如下

void Bellman(){
	memset(d, 0x3f, sizeof d);
	d[1] = 0;
	for(int i = 1; i <= n; i++){
		int flag = 0;
		for(int j = 1; j <= m; j++){
			int u = upoints[j], v = vpoints[j], w = cost[j];
			if(d[u] + w < d[v])
				d[v] = d[u] + w, flag= 1;
		}
		if(!flag)break;
	}
}

性质1:不存在负环的情况下, 有效更新最多进行n - 1次,计算的d[]就一定是最短路
解释:参考dijkstra算法, 边权为正数时,每次至少有一个点更新到最小值。
不同的是, bellman在有负边的情况下仍能正常工作, 只要不存在负环, 原因是是, 最短路一定是简单路径, 每次循环时, 能扩充出一个正确的点。

推论1:以最短路为例, 若第n次仍有边满足d[u] + w < d[v], 则存在负环, 最短路不可求

可以用队列优化:

void Spfa(){
	memset(d, 0x3f, sizeof d);
	d[1] = 0;
	que.push(1);
	vis[1] = 1;
	while(q,size()){
		int u = q.front();
		q.pop();
		vis[u] = 0;
		for(int i = head[u]; i; i = Next[i]){
			int v = ver[i], w = cost[i];
			if(d[u] + w < d[v]){
				d[v] = d[u] + w;
				if(!vis[v])vis[v] = 1, q.push(v);
			}
		}
	}
}

队列优化能用的原因仍可以用简单路径解释, 最短路上的第二个点离起点距离为1, 将所有距离为1的点更新完后, 再更新距离为2的点

知识点2:最长路
这个其实不难, 将不等号方向变相即可轻松实现。此时dijkstra不能判断正边, bellman可以判断有无正环

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值