CHAPTER_10 提高篇(4)——图算法专题
10.4.2Dijkstra算法
(接上篇)
下面通过两道例题,练习Dijkstra算法的运用。
题目1:
给出N个城市(城市用0,1,...,N-1编号),M条无向边,每条边代表连接两个城市的道路,边的权值代表其距离。每个城市中都有一定数目的救援小组,所有边的边权已知。现在给出起点和终点,求从起点到终点的最短路径条数及最短路径上的救援小组数目之和。
输入格式:
每个输入包含一个测试用例。对于每个测试用例,第一行给出4个正整数:城市数N(N<=500)、道路数目M、起点城市C1、终点城市C2。第二行给出N个整数,分别代表N个城市各自的救援小组数目。接下来M行分别给出每条道路的信息,每行包括三个整数,前两个表示该道路连接的两个城市编号,第三个整数表示该道路的距离。
输出格式:
对于每个输入,在一行内输出两个数字(中间用空格分隔):C1与C2间的最短路径数目、最短路径中救援小组之和的最大值。
输入样例:

5 6 0 2
1 2 1 5 3
0 1 1
0 2 2
0 3 1
1 2 1
2 4 1
3 4 1
输出样例:
2 4
思路:
该题使用无向图模型,首先为输入数据建立图模型,再用Dijkstra算法生成最短路径树即可得到起点C1到C2的最短路径。
但是基础的Dijkstra算法并不能完全解决这个问题,原因在于两点:题中要求输出最短路径的最大点权和,而基础的Dijkstra是没有考虑点权这个标尺的;在基础的Dijkstra中,起点S到另一点V只能生成一条最短路径,即没有考虑存在多条最短路径的情况,而题目中要我们输出最短路径数目。
要解决这两个问题,我们要对原Dijkstra算法进行两点小改动:
(1)为图模型新增点权。设置数组w[n],w[u]表示起点到顶点u的最短路径的最大点权和。初始化时令起点的w[]值为起点的权值,而其他顶点的w[]为0。
(2)要求最短路径数。设置数组num[n],num[u]表示从起点到顶点u的最短路径条数。初始时令起点的num[]为1,而其他顶点的num[]为0。
设置上面数组后,在Dijkstra中更新d[]的时候w[]和num[]只需跟着同步更新。
参考代码:
#include<iostream>
#include<vector>
#include<algorithm>
#define INF 0x3fffffff
using namespace std;
const int maxn=1001;
int n,m,c1,c2; //n为顶点数,m为道路数,c1起点,c2终点
int d[maxn]; //记录起点到每个顶点的最短距离
int weight[maxn]; //记录起点到每个顶点的最大点权和
int num[maxn]; //记录起点到每个顶点的最短路径数目
bool vis[maxn]={0}; //用来实现集合SET
struct path {
int v; //边的终点
int pathW; //边权
path(){}
path(int _v,int _pathW) {
v=_v;
pathW=_pathW;
}
};
struct graph { //图的邻接表
int nodeW; //点权
vector<path> Adj; //记录每个顶点的边
}G[maxn];
void Dijkstra() {
fill(d,d+n,INF);
d[c1]=0; //初始化d[]
fill(weight,weight+n,0);
weight[c1]=G[c1].nodeW; //初始化weight[]
fill(num,num+n,0);
num[c1]=1; //初始化num[]
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) { //找到未访问顶点中d[]最小的
u=j;
MIN=d[j];
}
}
if(u==-1) //找不到最小路径的邻接点,说明剩下的顶点与起点不连通
return;
vis[u]=1; //访问顶点u
for(int j=0;j<G[u].Adj.size();j++) { //遍历每个u能到达的顶点v
int v=G[u].Adj[j].v;
if(vis[v]==0) {
if(d[u]+G[u].Adj[j].pathW==d[v]) { //找到一条相同路径
if(weight[u]+G[v].nodeW>weight[v]) {
weight[v]=weight[u]+G[v].nodeW; //更新当前最大的weight[v]
}
num[v]+=num[u]; //更新num[]
}
if(d[u]+G[u].Adj[j].pathW<d[v]) {
d[v]=d[u]+G[u].Adj[j].pathW; //优化d[v]
weight[v]=weight[u]+G[v].nodeW; //更新weight[]
num[v]=num[u]; //到邻接点v的最短路径数目和u点相同
}
}
}
}
}
int main() {
int u,v,w;
cin>>n>>m>>c1>>c2; //输入数据的第一行
for(int i=0;i<n;i++) { //输入每个顶点的点权
cin>>G[i].nodeW;
}
for(int i=0;i<m;i++) { //输入每条边和边权
cin>>u>>v>>w;
G[u].Adj.push_back(path(v,w));
G[v].Adj.push_back(path(u,w));
}
Dijkstra();
cout<<num[c2]<<' '<<weight[c2]<<endl; //输出c1到c2的最短路径数和其中的最大点权
return 0;
}