本篇博文参考了博文:https://blog.csdn.net/m0_37579232/article/details/83039175
先回顾一下一般的迪杰斯特拉算法模板
//循环n-1次,每次有一个节点加入
for(int i = 0; i < n-1; i++)
{
//松弛操作,更新与新加入的结点newp直接相邻的结点的dist
for(int j = 0; j < edge[newp].size(); j++)
{
int t = edge[newp][j].next;
int c = edge[newp][j].weight;
//如果已经加入,则跳过
if(mark[t])
continue;
if(dist[t] == -1 || dist[newp] + c < dist[t])
dist[t] = dist[newp] + c;
}
//找到未加入最短路径集合的距离v0最短的结点
int min = 111111111;
for(int j = 0; j < n; j++)
{
if(mark[j])
continue;
if(dist[j] == -1)
continue;
if(dist[j] < min)
{
min = dist[j];
newp = j;
}
}
mark[newp] = true; //新加入的结点
}
dist数组的含义是从source到current的最短路径,我们可以类比dist数组,设置另外两个数组,count数组和tot数组,分别表示从source到current的最大求援人数和最短路径的个数。
从上面的松弛操作可以看出,对于一般的迪杰斯特拉算法,每个结点只计算了一次,因此为了计算从source到current的所有最短路径和最大救援人数,将松弛操作中的if(mark[])语句删除即可。因此变形后的松弛操作就是:
//更新与新加入的结点newp直接相邻的结点的dist
for(int j = 0; j < edge[newp].size(); j++)
{
int t = edge[newp][j].next;
int c = edge[newp][j].weight;
//如果dist[t] == dist[newp]+c说明有多条路径
if(dist[t] == dist[newp] + c)
{
tot[t] += tot[newp]; //多条路径
if(count[t] < count[newp] + num[t])
count[t] = count[newp] + num[t];
}
if(dist[t] == -1 || dist[newp] + c < dist[t])
{
dist[t] = dist[newp] + c;
count[t] = count[newp] + num[t];
tot[t] = tot[newp];
}
}
//AC代码
#include <iostream>
#include <vector>
using namespace std;
typedef struct Node
{
int next, weight;
};
vector<Node> edge[501]; //邻接链表
bool mark[501];
int dist[501];
int num[501]; //用于记录各个city的目前的救援人数
int count[501]; //用于保存到各个city的最多参加救援的人数
int tot[501]; //用于保存到各个city的路径个数
int main()
{
int n, m, c1, c2;
int x, y, z;
while(cin >> n >> m >> c1 >> c2)
{
//初始化邻接链表
for(int i = 0; i < n; i++)
edge[i].clear();
for(int i = 0; i < n; i++)
cin >> num[i];
Node tmp;
for(int i = 0; i < m; i++)
{
cin >> x >> y >> z;
tmp.next = y, tmp.weight = z;
edge[x].push_back(tmp);
tmp.next = x;
edge[y].push_back(tmp);
}
//初始化
for(int i = 0; i < n; i++)
{
mark[i] = false;
dist[i] = -1;
count[i] = num[i];
tot[i] = 0;
}
dist[c1] = 0;
mark[c1] = true;
int newp = c1;
tot[c1] = 1; //肯定至少有一条从c1到c2的最短路径
//循环n-1次,每循环一次,加入一个结点到最短路径集合
for(int i = 0; i < n-1; i++)
{
//更新与新加入的结点newp直接相邻的结点的dist
for(int j = 0; j < edge[newp].size(); j++)
{
int t = edge[newp][j].next;
int c = edge[newp][j].weight;
//如果dist[t] == dist[newp]+c说明有多条路径
if(dist[t] == dist[newp] + c)
{
tot[t] += tot[newp]; //多条路径
if(count[t] < count[newp] + num[t])
count[t] = count[newp] + num[t];
}
if(dist[t] == -1 || dist[newp] + c < dist[t])
{
dist[t] = dist[newp] + c;
count[t] = count[newp] + num[t];
tot[t] = tot[newp];
}
}
//找到未加入最短路径集合的距离v0最短的结点
int min = 111111111;
for(int j = 0; j < n; j++)
{
if(mark[j])
continue;
if(dist[j] == -1)
continue;
if(dist[j] < min)
{
min = dist[j];
newp = j;
}
}
mark[newp] = true;
}
printf("%d %d\n", tot[c2], count[c2]);
}
return 0;
}