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; //没了。
}
看完以后就关上它,自己写一遍,交到各大评测网站上吧! 传送门来着的