最短路问题(1)

最短路问题\((1)\)

闲扯

原因

本蒟蒻记性不太好怕忘(一样的说辞ヽ(ー_ー)ノ),咳,写这篇最短路的文章来扎实自己的基础(同时让自己搞的更明白点)。

背景

在做图论题的时候,我们往往会通过最短路来进行一些操作,在这篇和下篇文章里我将会介绍\(Dijkstra\)\(SPFA\)两种常见的方法来解决最短路问题。

\(Dijkstra\)

背景

\(Dijkstra\)可以说是最经典的最短路算法之一了,也是我们在进行算法比赛时在无负边权图时首选的算法(\(SPFA\)已废,\(Dijstra\)当立(划去))。这种算法是由荷兰计算机大神\(Edsger Wybe Dijkstra\)创造的方法,也以他的名字命名。

分析

\(Dijkstra\)的思路是:设置定点集合\(S\),首先将起始点加入该集合,然后根据起始点到其他定点的路径长度,选择长度最小的定点加入集合,根据所加入定点更新远点到其他定点的路径长度,然后再选取最小变的定点。一次来做,知道求解出到达所有定点的路径长度,然后再选取最小边的定点。一次来做,知道求解出到达所有定点的路径长度。(百度给的解释真是简洁晦涩(^_−)☆)。我是这样理解的,这时一个贪心的思路来做的,首先我们开一个数组记录将所有的路径权重\(dis[N]\)都设为\(Max\),我们找到我们的起点\(u\),将其表记起来,将起点的路径权重设为\(0\),然后将它所能直接到达的点将他们的路径权重设为\(dis[v] = w[u,v]\),然后我们将\(dis\)数组中的最小值,这个值就是起点到这个点的最小路径,我们将这个点添加到一个集合当中,这个顶点就不再需要更新了,接下来,我们观察新加入的顶点是否可以到达其他顶点,并且通过这个顶点到达是否距离比起点开始距离要近,如果是的话,我们就再将这个顶点添加入之前的集合当中,接下来就在此重复上述的操作,直到我们往里面添加顶点的数组中已经包含了图的所有顶点。

具体例子

接下来我们来看一个具体的例子来更好的理解\(Dijkstra\)算法。我们来看这样的一个场景。

在这张图中,我们需要求出从点\(1\)到点\(6\)的最短路径,在这里我们用之前提到的链式前向星来存图,不了解的可以看看链式前向星
这里我们首先找到起点\(1\)并将\(1\)点的距离设为\(0\),并标记上已访问。然后找出它可以直接到达的顶点\(2\)\(3\),并更新他们的距离。

这时我们看到到\(3\)的距离最小是\(3\)那么我们便将\(3\)这个点标记上。

这时我们再从点\(3\)出发,发现他可以到达\(2、4、5\)三个点,更新他们。

我们看到从点\(3\)到点\(2\)比从点\(1\)到点\(2\)的距离还要短,所以我们决定从\(1\)\(3\)\(2\),标记上点\(2\)

这时我们再从\(2\)点出发,与点\(2\)相连接的为4点,我们发现从\(2\)\(4\)比原来的距离要远,所以更新并标记点\(4\)

然后再更新与点\(4\)连接的点\(5\)和点\(6\)

我们可以发现点\(5\)无法再更新了,记录下点\(5\)

然后到达点\(6\),并标记。

这时我们发现所有的点已经都被标记了,所以我们就得到的最短路。

代码实现

#include<cstdio>
#include<cstring>
#include<algorithm>
#define INF 0x3f3f3f
using namespace std;
const int maxn = 1e5 + 10;
int n, m, cnt = 0;
int head[maxn], dis[maxn], vis[maxn];
struct edge{
	int to, next, w;
}e[maxn];
void add(int u, int v, int w){
	e[++cnt].next = head[u];
	e[cnt].to = v;
	e[cnt].w = w;
	head[u] = cnt;
}
void Dijkstra(){
	dis[1] = 0;
	for(int i = 1; i < n; i++){
		int minn = INF, now;
		for(int j = 1; j <= n; j++)
			if(!vis[j] && dis[j] < minn){
				minn = dis[j];
				now = j;
			}
		vis[now] = true;
		for(int j = head[now]; j; j = e[j].next){
			int to = e[j].to;
			if(!vis[to]) dis[to] = min(dis[to], dis[now] + e[j].w);
		}
	}
} 
int main(){
	memset(dis, INF, sizeof(dis));
	memset(vis, false, sizeof(vis));
	scanf("%d %d", &n, &m);
	for(int i = 1; i <= m; i++){
		int u, v, w; scanf("%d %d %d", &u, &v, &w);
		add(u, v, w);	//存储单向图,如果存储双向图的话加上add(v, u, w); 
	}
	int s = 1, t = n; //s为起点,w为终点
	Dijkstra();
	printf("%d\n", dis[t]);
	return 0; 
}

我们来模拟一遍这个图。

芜湖~可以看出我们得出了正确的结果。那么这就是\(Dijkstra\)算法的模板了,在做题过程中我们需要灵活的运用才能够取得理想的成绩。
完结撒花ヾ(✿゚▽゚)ノ

posted @ 2020-11-15 23:08  summitsoul  阅读(94)  评论(2编辑  收藏  举报