(」・ω・)」うー!(/・ω・)/にゃー!
——潜行吧奈亚子

最短路

最短路

概述

基本的模板大概就是给一个图,求任意两点间的最短路(两点间有不同的距离)。这时候,就需要用到一些神奇的算法

\(Floyd-Warshall\)

之前有一篇笔记讲过如何用\(O(n^2)\)的算法来进行求解最短路,但这时就有些人嫌深搜或广搜太麻烦(比如我)。那么这时候就需要学学新算法了:\(Floyd-Warshall\)算法!!!那问题来了,他的思路和原理是什么?

思路:

一般来说,稍微有些数学基础的人看到有些像三角形的图形就会想到两边之和大于第三边,那么这时候就会有一种想法:每个点都有可能使得另外两个顶点之间的路程变短。那么就可以有大概思路:

如果任意两点间不允许有第三点,则这些城市间的最短路就是初始路程

如果任意两点间允许有更多的点,则暴力枚举所有的点,并更新最短路

\(Floyd\)核心代码如下:

for (int k = 1; k <= n; k++)
{
	for (int i = 1; i <= n; i++)
	{
		for (int j = 1; j <= n; j++)
		{
			if (e[i][j] > e[i][k] + e[k][j])
			{
				e[i][j] = e[i][k] + e[k][j];				}
			}
		}
	}
}

完整代码:

#include <stdio.h>

int main()
{
	int e[10][10], k, i, j, n, m, t1, t2, t3;
	int inf = 99999999;//用inf(infinity的缩写)存储一个假定的正无穷
	//读入n和m,n表示顶点个数,m表示边的条数
	scanf("%d %d", &n, &m);
	//初始化
	for (int i = 1; i <= n; i++)
	{
		for (int j = 1; j <= n; j++)
		{
			if (i == j)
			{
				e[i][j] = 0;
			}
			else
			{
				e[i][j] = inf;
			}
		}
	}
	//读入边
	for (int i = 1; i <= m; i++)
	{
		scanf("%d %d %d", &t1, &t2, &t3);
		e[t1][t2] = t3;//加边,t3为两点间距离
	}
	//Floyd-Warshall算法核心代码
	for (int k = 1; k <= n; k++)
	{
		for (int i = 1; i <= n; i++)
		{
			for (int j = 1; j <= n; j++)
			{
				if (e[i][j] > e[i][k] + e[k][j])
				{
					e[i][j] = e[i][k] + e[k][j];
				}
			}
		}
	}
	//输出结果
	for (int i = 1; i <= n; i++)
	{
		for (int j = 1; j <= n; j++)
		{
			printf("%d ", e[i][j]);
		}
		printf("\n");
	}
	return 0;
}

\(Dijkstra\)-单源最短路

名字乍一看很可怕的样子,其实就是固定起点源点)的最短路。

基本的存储与弗洛伊德(\(Floyd-Warshall\))一样,用二维数组\(e\)来存储。但不同的是我们还需要一个\(dis\)数组来存储 源点到其余顶点的初始路程,专业点,叫做“估计值

基本思路:每次找到离原点最近的一个顶点,然后以该顶点为中心进行扩展,最终得到源点其余所有点的最短路径

基本步骤:
1. 将所有顶点分为两部分:已知最短路径的顶点集合\(P\)未知最短路径的顶点集合\(Q\)。最开始,已知最短路径的顶点集合\(P\)中只有源点一个顶点。这里\(book\)数组来记录哪些点在集合\(P\)中。例如对于某个顶点\(i\),如果\(book[i]\)\(1\)则表示这个顶点在集合\(P\)中,如果为\(0\)则表示这个顶点在\(Q\)

2. 设置源点\(s\)到自己的最短距离为\(0\)(即\(dis[s]=0\))。若存在有源点能直接到达的顶点\(i\),则把\(dis[i]\)设成\(e[s][i]\)。同时把所有其他(源点不能直接到达的)顶点的最短路径设为\(\infty\)

3. 在集合\(Q\)的所有顶点中选择一个离源点\(s\)最近的顶点\(u\)(即\(dis[u]\)最小)加入到集合\(P\)。并考察所有以点\(u\)为起点的边,对每一条边进行松弛发现两边之和小于第三边,于是把第三边更新为两边之和)操作。即如果\(dis[u]+e[u][v]<dis[v]\)则将\(dis[v]\)更新为\(dis[u]+e[u][v]\)

4. 重复第三步,若集合\(Q\)为空,算法结束,最后\(dis\)数组里的值就是源点到各个点的最短距离。

完整代码:

#include <stdio.h>
#include<iostream>
using namespace std;
int main()
{
	int e[10][10], dis[10],book[10],i, j, n, m, t1, t2, t3,u,v,min;
	int inf = 99999999;//用inf(infinity的缩写)存储一个假定的正无穷
	//读入n和m,n表示顶点个数,m表示边的条数
	scanf("%d %d", &n, &m);
	//初始化
	for (int i = 1; i <= n; i++)
	{
		for (int j = 1; j <= n; j++)
		{
			if (i == j)
			{
				e[i][j] = 0;
			}
			else
			{
				e[i][j] = inf;
			}
		}
	}
	//读入边
	for (int i = 1; i <= m; i++)
	{
		scanf("%d %d %d", &t1, &t2, &t3);
		e[t1][t2] = t3;//加边,t3为两点间距离
	}
	//初始化dis数组
	for (int i = 1; i <= n; i++)
	{
		dis[i] = e[1][i];
	}
	//book数组初始化
	memset(book, 0, sizeof(book));
	book[1] = 1;
	//Dijkstra算法核心代码
	for (int i = 1; i <= n; i++)
	{
		//找到离1号顶点最近的顶点
		min = inf;
		for (int j = 1; j <= n; j++)
		{
			if (book[j] == 0 && dis[j] < min)
			{
				min = dis[j];//更新最近的顶点
				u = j;
			}
		}
		book[u] = 1;
		for (int v = 1; v <= n; v++)
		{
			if (e[u][v] < inf)
			{
				dis[v] = max(dis[v], dis[u] + e[u][v]);
			}
		}
	}
	//输出结果
	for (int i = 1; i <= n; i++)
	{
		printf("%d ", dis[i]);
	}
	return 0;
}


posted @ 2021-12-25 22:13  GalaxyOier  阅读(8)  评论(0)    收藏  举报