Dijkstra 算法原始版本仅适用于找到两个顶点之间的最短路径,后来更常见的变体固定了一个顶点作为源结点然后找到该顶点到图中所有其它结点的最短路径,产生一个最短路径树。本算法每次取出未访问结点中距离最小的,用该结点更新其他结点的距离。需要注意的是绝大多数的Dijkstra 算法不能有效处理带有负权边的图。
const int maxv = 1000;
const int inf = 999999;
int n, e[maxv][maxv]; //e矩阵记录i和j点间的距离
//fill(e[0], e[0] + maxv * maxv, inf); 初始化e矩阵使任意两点间距离最大
int dis[maxv], pre[maxv]; //dis为出发点到当前结点的距离,pre用来记录当前结点的前一个结点
bool visit[maxv] = {false};//visit表示结点是否被访问
void Dijkstra(int start) //start为出发点
{
fill(dis, dis + maxv, inf); //dis初始化为无穷远
dis[start] = 0; //出发点到自身距离为0
for(int i = 0; i < n; i++)
pre[i] = i; //初始状态设每个点的前驱为自身
for(int i = 0; i < n; i++)
{
int u = -1, minn = inf; // u记录目前为止距离出发点最小的点 , minn记录目前为止距离出发点最小的点的距离
for(int j = 0; j < n; j++)
{
if(visit[j] == false && dis[j] < minn) //在目前所有可到的点中遍历找距离出发点最近的点
{
u = j;
minn = dis[j];
}
}
if(u == -1) return ; //没有更近的结点了,说明已经遍历完,退出
visit[u] = true; //标记遍历过的结点
for(int v = 0; v < n; v++)
{
if(visit[v] == false && e[u][v] != inf && dis[u] + e[u][v] < dis[v]) //更新最短距离
{
dis[v] = dis[u] + e[u][v];
pre[v] = u; //记录前一个结点
}
}
}
}
附上打印最短路径的代码
void dfs(int s, int v) //打印s到v的最短路径
{
if(v == s) {
printf("%d\n", s);
return ;
}
dfs(s, pre[v]); //利用pre数组
printf("%d\n", v);
}
常见的附加考法有:
一、第一标尺是距离,如果距离相等的时候,新增第二标尺(花费,时间等)
例如新增边权(第二标尺),要求在最短路径有多条时要求路径上的花费之和最小,只需修改更新最短距离的for循环
for(int v = 0; v < n; v++)
{
if(visit[v] == false && e[u][v] != inf)
{
if(dis[u] + e[u][v] < dis[v])
{
dis[v] = dis[u] + e[u][v];
c[v] = c[u] + cost[u][v];
}
else if(dis[u] + e[u][v] == dis[v] && c[u] + cost[u][v] < c[v])
{
c[v] = c[u] + cost[u][v];
}
}
}
二、给定点权,要求在最短路径上有多条时要求路径上的点权之和最大(类似第一种情况)
for(int v = 0; v < n; v++)
{
if(visit[v] == false && e[u][v] != inf)
{
if(dis[u] + e[u][v] < dis[v])
{
dis[v] = dis[u] + e[u][v];
w[v] = w[u] + weight[v];
}
else if(dis[u] + e[u][v] == dis[v] && w[u] + weight[v] > w[v])
{
w[v] = w[u] + weight[v];
}
}
}
三、直接问有多少条最短路径。增加一个数组num[],num[s] = 1,其余num[u] = 0,表示从起点s到达顶点u的最短路径的条数为num[u]
for(int v = 0; v < n; v++)
{
if(visit[v] == false && e[u][v] != inf)
{
if(dis[u] + e[u][v] < dis[v])
{
dis[v] = dis[u] + e[u][v];
num[u] = num[v];
}
else if(dis[u] + e[u][v] == dis[v])
{
num[v] = num[v] + num[u];
}
}
}
当然已经求得pre数组,就知道了所有的最短路径,然后要做的就是用dfs遍历所有最短路径,找出一条使第二标尺最优的路径
int optvalue;
vector<int> pre[maxv];
vector<int> path, temppath;
void dfs(int v) { // v为当前访问结点
if(v == start) {
temppath.push_back(v);
int value = 路径temppath上的value值;
if(value 优于 optvalue) {
optvalue = value;
path = temppath;
}
temppath.pop_back();
return ;
}
temppath.push_back(v);
for(int i = 0; i < pre[v].size(); i++)
dfs(pre[v][i]);
temppath.pop_back();
}