Dijsktra算法
Dijsktra算法的定义
Dijkstra(迪杰斯特拉)算法是典型的单源最短路径算法,用于计算一个节点到其他所有节点的最短路径。主要特点是以起始点为中心向外层层扩展,直到扩展到终点为止。在无向图 G=(V,E) 中,假设每条边 E[i] 的长度为 w[i],找到由顶点 V0 到其余各点的最短路径(单源最短路径)。
算法思想
设G=(V,E)是一个带权有向图,把图中顶点集合V分成两组,第一组为已求出最短路径的顶点集合(用S表示,初始时S中只有一个源点,以后每求得一条最短路径 , 就将加入到集合S中,直到全部顶点都加入到S中,算法就结束了),第二组为其余未确定最短路径的顶点集合(用U表示),按最短路径长度的递增次序依次把第二组的顶点加入S中。
在加入的过程中,总保持从源点v到S中各顶点的最短路径长度不大于从源点v到U中任何顶点的最短路径长度。此外,每个顶点对应一个距离,S中的顶点的距离就是从v到此顶点的最短路径长度,U中的顶点的距离,是从v到此顶点只包括S中的顶点为中间顶点的当前最短路径长度。
具体的实现过程
假设a为起始点,a到a的最短路径是0,a到b的最短路径是1,a到c的最短路径是3,a到d的最短路径是8,a到e的最短路径是11。
用Dijkstra实现单源最短路:
Dijkstra的思想是先初始化这样一张表(除了a之外,a到其他点的距离都是正无穷):
在表中选择一个路径最短的点a,从a出发,a到自己的距离是0,它往外有三条边,更新a到b、c、d的最短距离,a这一点已经考察完了,以后a点不会被选到,此时表更新为:
在这张表中再选一个最短距离(a到b),选出点b,它有两条边指向c和e,从a出发,若经过b到达c,则现在的距离是3,比之前从a直接到c大,所以更新最短距离,以后b点不会被选到,此时表更新为:
在这张表中再选一个最短距离(a到c),选出c点,c往外有两条边,此时到e的距离为23(经过c),比原来的51小,更新最短距离;此时到d的距离为8(经过c),比原来的10小,更新最短距离,以后c点不会被选到,此时表更新为:
在这张表中再选一个最短距离(a到d),选出d点,d往e有一条边,此时到e的距离为11(经过d),更新最短距离,此时表更新为:
在这张表中再选一个最短距离(a到e),它往外没有边,结束。所以这个表就是最后的结果,Dijkstra只有一个要求:不要出现权值加起来是负数的环即可。
权值是负数的话最短距离会变成负无穷(不停地转,最短距离会不断减小),比如:
模板题(一)
下面介绍一道模板题来实现这种基本的算法思想:
POJ - 2387 Til the Cows Come Home
大概意思:输入总共有n个点的无向图的边数和权值,求出从起点1到点n的最短距离。
朴素的dijkstra算法
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <string>
#include <cstring>
#include <map>
#include <queue>
using namespace std;
typedef long long LL;
const int N = 1005;
int n, m, g[N][N], dist[N], st[N]; //g数组是记录从某点到某点的权值,dist数组表示任意点到点1的最短距离,st数组是标记已求出最短路径的顶点
int read(){
int x, f = 1;
char ch;
while(ch = getchar(), ch < '0' || ch > '9') if(ch == '-') f = -1;
x = ch - '0';
while(ch = getchar(), ch >= '0' && ch <= '9') x = x * 10 + ch - 48;
return x * f;
}
int dijkstra(){
int i, j, t;
memset(dist, 0x3f, sizeof(dist)); //把所有点到起点最短距离初始化为正无穷
dist[1] = 0; //起点到起点的最短距离为0
for(i = 1; i <= n; i++){
//每轮循环得到一个确定最短路径的顶点
t = -1; //t是个临时变量,为了确定某个点到起点的最短距离,并且把其放入已求出最短路径的顶点的集合里面
for(j = 1; j <= n; j++){
if(!st[j] && (t == -