【解题思路】
Dijkstra算法的基础上要统计相同最短路径的条数,并且如果有多条相同边权的最短路径,输出具有最大点权的数目。
【解题过程】
首先,先回顾Dijkstra算法:
1.对于有权图,先确定起点start,创建一个数组dist[顶点个数],先把dist[start]设为0,其余各点为无穷大。
2.找出dist中最小的蓝点x,即离起点最近的点,把该点变白,即访问过该点(vist[x] = 1)。
3.遍历x的所有邻边y,如果起点经由x点到达y点所经历的路径更短,将dist
[y]值改为dist[x]+G[x][y]。
4.重复2,3两步,直到所有点都变白。这一步也就是算法要循环n次的原因。
代码如下:
//Dijkstra模版
void Dijkstra(int s){ //s为起点
fill(d, d+MAXV, INF); //fill函数将整个d数组赋为INF
d[s] = 0;
for(int i=0; i<n; i++){ //循环n次
int u = -1; MIN = INF; //u使d[u]最小,MIN存放该最小的d[u]
for(int j=0;j<n;j++){ //找到未访问的顶点中d[]最小的
u = j;
MIN = d[j];
}
//找不到小于INF的d[u], 说明剩下的顶点和起点不连通
if(u == -1) return;
vis[u] = true; //标记u已访问
for(int v=0;v<n;v++){
//如果v未访问 并且 u能达到v 并且 以u为中介点可以使d[v]更优
if(vis[v] == false && G[u][v] != INF && d[u] + G[u][v] < d[v]){
d[v] = d[u] + G[u][v]; //更新d[v]
}
}
}
}
接着,这道题需要在模版基础上进行修改。题目要求找到如果最短路径有多条就要求给出最短路径的条数,并从找出具有最大点权的一条,输出最大点权。所以需要在原有代码的基础上创建两个数组:num[]和w[]。num用于记录每点到达起点的最短路径数目,w用于记录每点到达起点最短路径的最大点权值为多少。
在遍历x的所有邻接边的时候,如果经由x点到v点,距离更短,那num[v]=num[x](即到达v的路径条数和x的路径条数一致);如果经由x点到达v点的距离和v到原地距离一样,那么num[v]的值需要再加上num[x]。这一层还需要做一个判断,如果经由x到达v点的路径的点权之和大于原来v点的点权值,那么更新w[v]的值。
值得注意的是,当选择codeblock时,我在main函数里创建G[510][510]出现了报错,大概是超出了阈值,所以要把这个二维数组放在全局变量之中。dev则不需要这样
【完整代码】
#include <cstdio>
//memset函数的头文件
#include <cstring>
//fill函数的头文件
#include <algorithm>
using namespace std;
const int MAXV = 510;
const int INF = 100000000;
int G[MAXV][MAXV];
int n, m, st, ed; //n点数 m边数 st起点 ed终点
int weight[MAXV]; //点权
int num[MAXV], w[MAXV], d[MAXV]; //num最短路径条数 w最大点权之和 d最短距离
bool vis[MAXV];
void Dijkstra(int s){ //s为起点
fill(d, d+MAXV, INF);
fill(num, num+MAXV, 0);
fill(w, w+MAXV, 0);
//关于起点的初始化
d[s] = 0;
w[s] = weight[s];
num[s] = 1;
for(int i=0;i<n;i++){
int u=-1, MIN = INF;//u使d[u]最小,MIN存放该最小的d[u]
for(int j=0;j<n;j++){ //找到未访问的顶点中d[]最小的,即离start点最近的点
if(vis[j] == false && d[j] < MIN){
u = j;
MIN = d[j];
}
}
//找不到小于INF的d[u],说明剩下的顶点和起点s不连通
if(u == -1) return; //函数体内return表示终结该函数
vis[u] = true; //标记u为已访问
for(int v=0;v<n;v++){
//如果v未访问 并且 u能达到v 并且 以u为中介可以使d[v]更小
if(vis[v] == false && G[u][v] != INF){
if(d[u] + G[u][v] < d[v]){
d[v] = d[u] + G[u][v];
w[v] = w[u] + weight[v];
num[v] = num[u];
}else if(d[u] + G[u][v] == d[v]){ //找到一条相同长度的路径
if(w[u] + weight[v] > w[v]){ //以u为中介点时点权之和更大
w[v] = w[u] + weight[v];
}
//最短路径条数与点权无关,必须写在外面
num[v] += num[u];
}
}
}
}
}
int main(){
scanf("%d%d%d%d", &n, &m, &st, &ed);
for(int i=0; i<n; i++){
scanf("%d", &weight[i]); //读入点权,即每个城市的救援组数量
}
int u, v;
fill(G[0], G[0]+MAXV*MAXV, INF); //初始化图G,这里G为二维数组所以要*的形式
for(int i=0;i<m;i++){
scanf("%d%d", &u, &v);
scanf("%d", &G[u][v]); //读入边权
G[v][u] = G[u][v];
}
Dijkstra(st); //Dijkstra算法
printf("%d %d", num[ed], w[ed]);
return 0;
}