CHAPTER_10 提高篇(4)——图算法专题
10.4.2Dijkstra算法
(接上篇)
下面通过一道例题来练习Dijkstra+DFS模板的运用。
题目:
有N个城市(编号为0-N-1)、M条道路(无向边),每条道路连接两个不同的城市,并给出M条道路的距离属性与花费属性。现在给定起点S与终点D,求从起点到终点的最短路径、最短距离及花费。注意:如果有多条最短路径,则选择花费最小的那条。
输入格式:
每个输入包含一个测试用例。对于每个测试用例,第一行给出四个整数:城市个数N、道路数目M、起点编号S、终点编号D。接下来给出M行,每行包括一条道路基本信息:连接城市C1、连接城市C2、道路距离Distance、道路花费Cost。其中所有整数都小于等于500,每个整数间用空格分隔。
输出格式:
对于每个输入,在一行内输出花费最小的最短路径,然后输出其最短距离和总花费,每个数字间用空格分隔,且输入结尾没有多余的空格。
输入样例:
4 5 0 3
0 1 1 20
1 3 2 30
0 3 4 10
0 2 2 20
2 3 1 20
输出样例:
0 2 3 3 40
思路:
首先根据输入数据构建无向图模型。接着使用上面所讲解的Dijkstra算法求解pre[]数组(这段模板可以直接默写)。
在获得记录了最短路径前驱结点的pre[]数组后,我们开始编写DFS函数。编写DFS的过程同样是套模板,我们设置全局遍历minCost记录路径花费的最小值(即第二标尺最优值),当递归到达叶子结点的时候计算当前路径的花费总和tempCost,如果当前tempCost小于minCost,则更新minCost的值,并将当前路径记为最短路径。
通过Dijkstra+DFS递归我们最终能获得所需要的最短路径path,然后逆序遍历输出即可。
参考代码:
#include<iostream>
#include<vector>
#include<algorithm>
using namespace std;
const int maxn=1000;
const int INF=0x3fffffff;
int n,m,S,D; //城市个数n、道路数目m、起点s、终点d
int d[maxn]; //记录最短路径
bool vis[maxn]={0};
vector<int> pre[maxn]; //记录到达当前结点最短路径的前驱结点
vector<int> path,tempath; //最优路径,临时路径
int minCost=INF;
struct node {
int v; //边的终点
int dis,cost; //距离distance,花费cost
node(){}
node(int _v,int _dis,int _cost) {
v=_v;
dis=_dis;
cost=_cost;
}
};
vector<node> Adj[maxn]; //图的邻接表
void Dijkstra() { //模板:Dijkstra获得pre[]
fill(d,d+n,INF);
d[S]=0;
for(int i=0;i<n;i++) {
int u=-1, MIN=INF;
for(int j=0;j<n;j++) {
if(vis[j]==0&&d[j]<MIN) {
u=j;
MIN=d[j];
}
}
if(u==-1)
return;
vis[u]=1;
for(int j=0;j<Adj[u].size();j++) {
int v=Adj[u][j].v;
if(vis[v]==0&&d[u]+Adj[u][j].dis<d[v]) {
d[v]=d[u]+Adj[u][j].dis;
pre[v].clear();
pre[v].push_back(u);
}
else if(d[u]+Adj[u][j].dis==d[v]) {
pre[v].push_back(u);
}
}
}
}
void DFS(int v) { //DFS模板
if(v==S) { //形成一条最短路径
tempath.push_back(v);
int tempCost=0; //存放这条路径第二标尺:cost
for(int i=tempath.size()-1;i>0;i--) { //计算这条路径上的cost和
int id=tempath[i],j;
for(j=0;j<Adj[id].size();j++) { //找到点tempath[i]到点tempath[i-1]的边
if(Adj[id][j].v==tempath[i-1])
break;
}
tempCost+=Adj[id][j].cost; //累加计算cost
}
if(tempCost<minCost) { //如果当前最短路径的cost的更小
minCost=tempCost; //更新最短路径花费
path=tempath; //更新最短路径
}
tempath.pop_back();
return;
}
tempath.push_back(v);
for(int i=0;i<pre[v].size();i++) {
DFS(pre[v][i]);
}
tempath.pop_back();
}
int main() {
cin>>n>>m>>S>>D;
int u,v,dis,cost; //临时存储每条道路信息
for(int i=0;i<m;i++) { //根据输入构建图
cin>>u>>v>>dis>>cost;
Adj[u].push_back(node(v,dis,cost));
Adj[v].push_back(node(u,dis,cost));
}
Dijkstra();
DFS(D);
for(int i=path.size()-1;i>=0;i--) { //逆序输出最优路径
cout<<path[i]<<' ';
}
cout<<d[D]<<' '<<minCost; //输出最短路径值和最小花费
return 0;
}
还有一点需要提及,本题使用邻接矩阵实现比邻接表更为方便,空间条件上也允许使用邻接矩阵。邻接矩阵的解法和邻接表只有少许区别,留作读者自行思考。