算法学习12-搜索与图论03:最短路问题

算法学习12:最短路问题



前言

😊暂时只用记住:dijkstra和spea算法😊

在这里插入图片描述


在这里插入图片描述


在这里插入图片描述



在这里插入图片描述



在这里插入图片描述


提示:以下是本篇文章正文内容:

一、朴素Dijkstra:(稠密图:邻接矩阵)(邻接矩阵)


在这里插入图片描述



给定n个点,m条边的有向图,所有边均为正值。
请你求出1号点到n号点的最短距离,如果无法从1号点走到n好点,输出-1.
//1 < n <= 500, 1 < m <= 10^5 (稠密图:邻接矩阵)


#include <iostream>
#include <cstring>
#include <algorithm>

using namespace std;

const int N = 500 + 10;

// //1 < n <= 500,  1 < m <= 10^5 (稠密图:邻接矩阵)
int n, m;
int g[N][N];// g[i][j]:i到j的边长 
int dist[N];// 距离 
bool st[N];// 确定最短路

// 求出1号点到n号点的最短距离
int dijkstra()
{
	memset(dist, 0x3f3f3f, sizeof dist);
	dist[1] = 0;
	for(int i = 0; i < n; i ++)
	{
		int t = -1;
		// 当前没有确定最短路的点中,距离最小的那一个。 
		for(int j = 1; j <= n; j ++)// 编号:1 ~ n 
			if(!st[j] && (t == -1 || dist[t] > dist[j]))
				t = j;// 如果当前t不是最短的,就把t变成j 
				
		if(t == n) break; 
			
		st[t] = true;
		// 用t更新其它点的距离	
		for(int j = 1; j <= n; j ++)// 迭代其他点 
			// 用1到t的距离+t到j的距离 去更新 1到j的距离 
			dist[j] = min(dist[j], dist[t] + g[t][j]);
	}
	if(dist[n] == 0x3f3f3f) return -1;
	return dist[n];
 } 

int main()
{
	scanf("%d%d", &n, &m);
	memset(g, 0x3f3f3f, sizeof g);
	
	while(m --)
	{
		// 添加边 
		int a, b, c;
		scanf("%d%d%d", &a, &b, &c);
		// 因为初始化赋值0x3f3f3f 
		g[a][b] = min(g[a][b], c);
	}
	
	int t = dijkstra();
	printf("%d\n", t); 
	return 0;
}

在这里插入图片描述



二、堆优化版Dijkstra:(稀疏图:邻接表)(优先队列实现小顶堆)


在这里插入图片描述


给定n个点,m条边的有向图,所有边均为正值。
请你求出1号点到n号点的最短距离,如果无法从1号点走到n好点,输出-1.
1 < n,m <= 10^5(稀疏图:邻接表)


#include <iostream>
#include <cstring>
#include <algorithm>
#include <queue> 

using namespace std;
// 使用堆维护所有点的距离,须要知道结点编号,堆里面存储的是pair
// pair<距离, 编号>
typedef pair<int, int> PII;

const int N = 1e5 + 10;

int n, m;
int h[N], e[N], w[N], ne[N], idx;// 稀疏图:邻接表 
int dist[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 ++;
}


int dijkstra()
{
	// 设置其他点距离为“正无穷”,1号点距离为0 
	memset(dist, 0x3f3f3f, sizeof dist);
	dist[1] = 0;
	// 优先队列实现小顶堆 
	priority_queue<PII, vector<PII>, greater<PII>> heap;
	heap.push({0, 1});// 添加举例为0,编号为1的点
	
	while(heap.size())
	{
		// 当前没有确定最短路的点中,距离最小的那一个
		auto t = heap.top();
		heap.pop();
		
		int num = t.second, distance = t.first;// 编号,距离
		if(st[num]) continue;// 结点冗余(出现过)
		
		// 用t取更新其他点的距离:遍历这个点的所有邻边 
		for(int i = h[num]; i != -1; i = ne[i])// 遍历邻接结点 
		{
			int j = e[i];// 取邻接结点“编号”
			// 用1到t的距离+t到j的距离 去更新 1到j的距离  
			if(dist[j] > distance + w[i]) 
			{
				dist[j] = distance + w[i];
				heap.push({dist[j], j});
			}
		 } 
	 } 
	if(dist[n] == 0x3f3f3f) return -1;
	return dist[n];
 } 

int main()
{
	scanf("%d%d", &n, &m);
	memset(h, -1, sizeof h);
	
	while(m --)
	{
		int a, b, c;
		scanf("%d%d%d", &a, &b, &c);
		add(a, b, c);
	}
	
	int t = dijkstra();
	printf("%d\n", t); 
	return 0;
}

在这里插入图片描述

三、(负权图)Bellman_Ford算法:有边数限制的最短路

在这里插入图片描述



在这里插入图片描述



(有边数限制的最短路)
给定n个点,m条边的有向图,图中可能存在重边和自环,注意边权可能是负数。
请你求出从1号点到n号点(最多经过k条边)的最短距离,如不没有输出impossible。


// 给定n个点,m条边的有向图,图中可能存在重边和自环,注意边权可能是负数。
// 请求出从1号点到n号点(最多经过k条边)的最短距离,如果没有,输出impossible。
#include <iostream>
#include <cstring>
#include <algorithm>

using namespace std;

const int N = 500 + 10, M = 1e4 + 10;

int n, m, k;
int dist[N], backup[N];// dist:距离  backup:每一次迭代前的"备份" 

// 存储边的信息 
// a 到 b 的权值是 w 
struct Edge
{
	int a, b, w;
 }edges[M];

int bellman_ford()
{
	memset(dist, 0x3f3f3f, sizeof dist);
	dist[1] = 0;
	// 最多经过k条边,迭代k次 
	for(int i = 0; i < k; i ++)
	{
		memcpy(backup, dist, sizeof dist);// 备份 
		for(int j = 0; j < m; j ++)
		{
			int a = edges[j].a, b = edges[j].b, w = edges[j].w;
			// 到b的距离 vs 到a的距离+a到b的距离 
			dist[b] = min(dist[b], backup[a] + w);
		}
	}
	// 最后保证:dist[b] <= back[a] + w
	if(dist[n] > 0x3f3f3f / 2) return -1;
	return dist[n];
}

int main()
{
	scanf("%d%d%d", &n, &m, &k);
	// 赋值时,0~m-1,取出时, 0~m-1
	for(int i = 0; i < m; i ++)
	{
		int a, b, w;
		scanf("%d%d%d", &a, &b, &w);
		edges[i] = {a, b, w};
	}
	
	int t = bellman_ford();
	
	if(t == -1) puts("impossible");
	else printf("%d\n", t); 
	
	return 0;
}

在这里插入图片描述

四、SPFA(spfa求最短路,要求没有负环)

思路:(更新过谁,就拿这个点去更新别人)
**思考:推荐使用spfa算法,正权图也可以使用。
注意:如果使用“spfa算法”被卡了(出现“网格形状的图”),那就换成dijkstra算法。


在这里插入图片描述



在这里插入图片描述



在这里插入图片描述

例题1:spfa算法求最短路:


给定n个点,m条边的有向图,图中可能存在重边和自环,注意边权可能是负数。
请你求出从1号点到n号点的最短距离,如果没有输出impossible。


#include <iostream>
#include <cstring>
#include <algorithm>
#include <queue>

using namespace std;

const int N = 1e5 + 10;

int n, m;
int h[N], w[N], e[N], ne[N], idx;
int dist[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 ++;
}

// 使用限制:图中没有负环,可以代替“dijkstra算法” 
int spfa()
{
	// 将距离全部设置为 “正无穷” 
	memset(dist, 0x3f3f3f, sizeof dist);
	dist[1] = 0;
	
	queue<int> q;
	q.push(1);
	st[1] = 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;
				}
			 } 
		}
	}
	if(dist[n] == 0x3f3f3f) return -1;
	return dist[n];
}

int main()
{
	// 邻接表的元素全部初始化为 -1 
	scanf("%d%d", &n, &m);
	memset(h, -1, sizeof h);
	
	// 插边 + 权值 
	while(m --)
	{
		int a, b, c;
		scanf("%d%d%d", &a, &b, &c);
		add(a, b, c);
	}
	
	int t = spfa();
	if(t == -1) puts("impossible"); 
	else printf("%d\n", t); 
	return 0;
}

在这里插入图片描述



例题2:spfa算法:《判断是否存在“负环”》

由“抽屉原理”知道:一共有n个点,出现n+1次时,必定有两个点重复出现。

在这里插入图片描述



给定n个点,m条边的有向图,图中可能存在重边和自环,注意边权可能是负数。
请你判断图中是否存在“负权回路”。


#include <iostream>
#include <cstring>
#include <algorithm>
#include <queue> 

using namespace std;

const int N = 1e5 + 10;

int n, m;
int h[N], w[N], e[N], ne[N], idx;// 邻接表 
int dist[N], cnt[N];// dist:距离 cnt:边数
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 spfa()
{	
	queue<int> q;
	// q.push(1);
	// st[1] = true;
	
	// 注意1:从1出发,我们可能到不了“负环”
	// 要遍历所有点 
	for(int i = 1; i <= n; i ++)
	{
		q.push(i);
		st[i] = 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];
				cnt[j] = cnt[t] + 1;// 更新边数!!! 
				
				// 由“抽屉原理”,判断“负环”,边重复出现 
				if(cnt[j] >= n) return true; 
				if(!st[j])
				{
					q.push(j);
					st[j] = true;
				}
			 } 
		}
	}
	return false;// 循环结束,表示不存在 负环 
}


int main()
{
	scanf("%d%d", &n, &m);
	memset(h, -1, sizeof h);
	
	while(m --)
	{
		int a, b, c;
		scanf("%d%d%d", &a, &b, &c);
		add(a, b, c);
	}
	
	if(spfa()) puts("Yes");
	else puts("No"); 
	return 0;
 } 

在这里插入图片描述

五、Floyd算法

在这里插入图片描述


:给定n个点,m条边的有向图,图中可能存在重边和自环,注意边权可能是负数。
在给定k个询问,每个询问包含两个整数x和y,表示查询x到y的最短距离,如果路径不存在,则输入出“impossible”。(数据保证图中,不存在负权回路)

1 <= n <= 200, 1 <= m < n^2(稠密图,邻接)矩阵


// 1  <= n <= 200, 1 <= m < n^2(稠密图,邻接)矩阵 
#include <iostream>
#include <algorithm>
#include <cstring>

using namespace std;

const int N = 210, INF = 1e9;

int n, m, Q;
int d[N][N];// 邻接矩阵 

// dp优化??? 
void floyd()
{
	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]);
}

int main()
{
	scanf("%d%d%d", &n, &m, &Q);
	// 初始化 邻接矩阵 
	for(int i = 1; i <= n; i ++)
		for(int j = 1; j <= n; j ++)
			if(i == j) d[i][j] = 0;// 对角线
			else d[i][j] = INF;// 正无穷
	// 插入边 + 权值		
	while(m --)
	{
		int a, b, w;
		scanf("%d%d%d", &a, &b, &w);
		d[a][b] = min(d[a][b], w);// 重边取最小 
	 } 
	 
	 floyd();
	 
	 while(Q --)
	 {
	 	int x, y;
	 	scanf("%d%d", &x, &y);
	 	// 中间可能会更新,然后比INF小那么“一点点” 
	 	if(d[x][y] > INF / 2) puts("impossible");
		else printf("%d", d[x][y]);
	 }
	return 0;
 } 

在这里插入图片描述


总结

提示:这里对文章进行总结:

💕💕💕

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

lennard-lhz

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值