最短路板子第一弹·Dijkstra基础

本文介绍了Dijkstra算法的基础知识,包括算法思路和核心操作——松弛。Dijkstra算法是一种基于贪心思想的单源最短路算法,与Prim算法类似。通过松弛操作替换更短边来不断更新节点的最短距离。文章提供了代码示例并鼓励读者理解并亲自实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1. Dijkstra

无图警告

1.1 思路

  Dijkstra的来历不必多讲,但是需要知道的,是Dijkstra是一种基于“贪心“的求“单源最短路”算法,和最小生成树中的Prim是类似的。(如果你不知道什么是单源最短路,建议百度一下再来)
  粗略来讲,Dijkstra的做法就是:

以起始点为中心向外层层扩展,直到扩展到终点为止 ——百度百科

  既然是这样,我们的松弛操作就该登场了

1.2 最短路的灵魂——松弛

  何谓松弛?简单讲,一根变成两根,它不就松了吗?也就是说,把一条边用几条比它更短的边来替代,就是松弛了。
  于是就出现了诸如这样的代码块:
if(d[i] > d[j] + w[i, j])

{

	d[i] = d[j] + w[i, j];

}
  这里的 d [ i ] d[i] d[i]代表 i i i点到出发点的最短距离,即答案。
  这里的 w [ i , j ] w[i, j] w[i,j]代表 i i i点到 j j j点的已经存在的路径的长度,就是读进来的那个,先用邻接矩阵存着。

Ps:如果你刚刚学,建议在这里停下来用至少5分钟“用心”想想为什么,可以画画图,查查相关资料(比如别人的Blog)。真正的学懂,一定是理解,能够自己推出来,而不是单纯的记忆。

1.3 完整代码+注释

相信理解过以上两点的基本上就懂了,所以就不多解释了。(逃

不过啊,前方大量注释预警:(代码太丑,就忍着点看吧qwq)

//这个代码其实不长, 但是注释多了, 代码看起来就长了。

//这里是头文件。

#include<bits/stdc++.h>
using namespace std;

//这里是懒得打的东西。

typedef double dd;
typedef long long ll;

//这里是for循环和清空数组的函数。

#define fora(i, j, k) for(int i = j; i <= k; ++i)
#define forb(i, j, k) for(int i = j; i >= k; --i)
#define Set(a, b) memset(a, b, sizeof(a))

//这里是比大小。

inline ll lax(ll x, ll y) {return x > y ? x : y;}
inline ll lin(ll x, ll y) {return x < y ? x : y;}

//这里是快读。

inline ll read()
{
	char c = getchar(); ll s = 0; int tip = 1;
	for(; c < '0' || c > '9'; c = getchar()) if(c == '-') tip = -1;
	for(; c >= '0' && c <= '9'; c = getchar()) s = s * 10 + c - '0';
	return s * tip;
}

//从这里开始看就好, 上面都是板子(逃)。

const int maxn = 10000 + 10;
const int maxm = 500000 + 10;
const int INf = 2147483647;

/*
	变量说明:
		a[], b[], c[], u[], v[] 就是那个叫邻接表的(蒟蒻不会链式前向星什么的)。
		d[] 在上文解释了,答案变量(存单源最短路长度)。
		book[] 标记数组。
		n 点数, m 边数, k 出发点。
		i, j 没有用到的循环变量。
		mind, t 下文解释。
	结束。
*/

int i, j, k, m, n, mind, t;
int a[maxm], b[maxm], c[maxm], u[maxn], v[maxm], d[maxn];
bool book[maxn];

//文件操作应该不用多讲, 不过这里也没有用到。

inline void file()
{
	freopen("dijkstra.in", "r", stdin);
	freopen("dijkstra.out", "w", stdout);
}

//初始化部分。

inline void init()
{
	//读入n, m。
	n = read(), m = read(), k = read();
	//读入边,并且插入邻接表。
	fora(i, 1, m)
	{
		a[i] = read(), b[i] = read(), c[i] = read();
		v[i] = u[a[i]], u[a[i]] = i;
	}
	//初始化d数组
	fora(i, 1, n) if(i != k) d[i] = INf;
}

//主要操作部分。
/*
	注意这里!!!!!!
*/

inline void work()
{
	//循环n次,代表到n个点的最短路,也就是把n个点都标记掉。
	fora(i, 1, n)
	{
		//这里是找未标记的离初始点最近的点
		mind = INf, t = 0;
		fora(j, 1, n) if(book[j] == 0 && d[j] < mind) mind = d[j], t = j;
		//这里是打标记
		book[t] ^= 1;
		//这里是对 经过打标记的点的边 的 另一端点 的 答案(路径) 进行松弛 (这句话看着代码多读几遍)
		int j = u[t];
		for(; j != 0; j = v[j]) if(book[b[j]] == 0 && d[b[j]] > d[t] + c[j]) d[b[j]] = d[t] + c[j];
	}
}

//输出部分。

inline void write()
{
	fora(i, 1, n) printf("%d ", d[i]);
    cout << endl;
}

//主程序部分。

int main()
{
//	file();
	init(); //初始化部分。
	work(); //主要操作部分。
	write(); //输出部分。
	return 0; //没了。
}

看完以后就关上它,自己写一遍,交到各大评测网站上吧! 传送门来着的

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值