基本知识
图的存储(邻接矩阵)
一个N*N的二维数组行号表示起点,列号表示终点,对应的数值代表一条路径的权值。
解法
基本思想
- 从原点V0出发,一次所能到达的对应最短路径的终点(记为V1)一定是直达的,并且这个最短路径的权值最小。(反证法)
- 假如不是所求终点,这次就找第二短的路径。有如下两种情况:要么从V0直达,要么从V0经过V1到达。
- 松弛操作:依次比较从 V0转到V1再到剩下所有终点的距离 以及 V0直达剩下所有点的距离(所有未连线的点之间的距离都初始化为正无穷),不断更新两个地点之间的最短距离,随后选出一个第二近的终点(记为V2)。
- 在选择第三短的路径时,只需要比较 曾经记忆下来的的最短路径 和 从原点经过V2再到达 的长短即可。
Dijkstra算法具体实现
- 设置辅助数组Dist[],用来保存起点到各个终点的最短距离,每一项都初始化为正无穷。
- 状态转移方程:Dist[k] = min(Dist[k], Map[u][k] + Dist[u])
经典例题
畅通工程续
解:Dijkstra算法。
#include <cstdio>
#define INF 0x7FFFFFFF //int类型的最大值:2^31-1
using namespace std;
int n, m, a, b, distance, mini, start, target, dist[201], isConsidered[201], next, map[201][201];
int min(int x, int y) { return x <= y ? x : y; }
int main()
{
while(scanf("%d%d", &n, &m) == 2)
{
//初始化
for(int i = 0; i < n; i++)
{
dist[i] = INF;
isConsidered[i] = 0; //每个城市开始时都没有考虑过从原点到它的最小路径
for(int j = 0; j < n; j++)
map[i][j] = INF; //开始时没有路连接任意两个城市
}
for(int i = 0; i < m; i++)
{
scanf("%d%d%d", &a, &b, &distance);
map[a][b] = min(map[a][b], distance); //严谨!只记录两地之间最短路径
map[b][a] = map[a][b]; //题目为无向图
}
scanf("%d%d", &start, &target);
dist[start] = 0;
isConsidered[start] = 1;
//核心Dijkstra算法(迪杰斯特拉算法)
while(start != target)
{
mini = INF;
for(int i = 0; i < n; i++) //松弛操作!
{
if(map[start][i] != INF) //如果有路
dist[i] = min(dist[i], dist[start]+map[start][i]);
if(!isConsidered[i] && dist[i] < mini)
{
next = i;
mini = dist[i];
}
}
if(mini == INF) break; //之后的都到不了了
start = next;
isConsidered[start] = 1;
}
//输出结果
if(dist[target] == INF) printf("-1\n");
else printf("%d\n", dist[target]);
}
return 0;
}
一个人的旅行
解:把相邻的城市都初始化为距离起点(虚拟起点)为0的点,再使用Dijkstra算法即可。
#include <cstdio>
#define INF 0x7FFFFFFF
using namespace std;
int a, b, time, Min, start, next, target, fakeStart, fakeDest, trackNum, adjoinNum, destNum, map[1002][1002], dist[1002], isConsidered[1002];
int min(int x, int y) { return x <= y ? x : y; }
int main()
{
while(scanf("%d%d%d", &trackNum, &adjoinNum, &destNum) == 3)
{
for(int i = 0; i <= 1001; i++)
{
for(int j = 0; j <= 1001; j++)
map[i][j] = INF;
isConsidered[i] = 0;
dist[i] = INF;
}
for(int i = 0; i < trackNum; i++)
{
scanf("%d%d%d", &a, &b, &time);
map[a][b] = min(map[a][b], time);
map[b][a] = map[a][b];
}
fakeStart = 0;
fakeDest = 1001;
dist[fakeStart] = 0;
isConsidered[fakeStart] = 1;
for(int i = 0; i < adjoinNum; i++)
{
scanf("%d", &a);
map[fakeStart][a] = 0;
map[a][fakeStart] = 0;
}
for(int i = 0; i < destNum; i++)
{
scanf("%d", &b);
map[b][fakeDest] = 0;
map[fakeDest][b] = 0;
}
start = fakeStart;
target = fakeDest;
while(start != target)
{
Min = INF;
for(int i = 0; i <= 1001; i++)
{
if(map[start][i] != INF)
dist[i] = min(dist[i], dist[start]+map[start][i]);
if(!isConsidered[i] && dist[i] < Min)
{
Min = dist[i];
next = i;
}
}
if(Min == INF) break;
start = next;
isConsidered[next] = 1;
}
if(dist[target] == INF) printf("-1\n");
else printf("%d\n", dist[target]);
}
return 0;
}