L2-001 紧急救援
作为一个城市的应急救援队伍的负责人,你有一张特殊的全国地图。在地图上显示有多个分散的城市和一些连接城市的快速道路。每个城市的救援队数量和每一条连接两个城市的快速道路长度都标在地图上。当其他城市有紧急求助电话给你的时候,你的任务是带领你的救援队尽快赶往事发地,同时,一路上召集尽可能多的救援队。
输入格式:
输入第一行给出4个正整数N、M、S、D,其中N(2≤N≤500)是城市的个数,顺便假设城市的编号为0 ~ (N−1);M是快速道路的条数;S是出发地的城市编号;D是目的地的城市编号。
第二行给出N个正整数,其中第i个数是第i个城市的救援队的数目,数字间以空格分隔。随后的M行中,每行给出一条快速道路的信息,分别是:城市1、城市2、快速道路的长度,中间用空格分开,数字均为整数且不超过500。输入保证救援可行且最优解唯一。
输出格式:
第一行输出最短路径的条数和能够召集的最多的救援队数量。第二行输出从S到D的路径中经过的城市编号。数字间以空格分隔,输出结尾不能有多余空格。
输入样例:
4 5 0 3
20 30 40 10
0 1 1
1 3 2
0 3 3
0 2 2
2 3 2
输出样例:
2 60
0 1 3
本题使用dijkstra来找最短路径,然后对Relax操作稍加修改,使其总是找出最短路径下可以召集的最大救援队数量和其最短路径数量即可。
简单提一下,城市道路是双向的,假如用二维数组E来记录边的长度,对于0 1 3,有E[0,1]=3 和E[1,0]=3
然后讲一下最短路径数量,以下图为例
从0至3的最短路径分别为0-2-3,0-3,0-1-3,它们的路径长度均为2,有从0至3的最短路径数量3。从0至4的最短路径数量为4。
为了更新最短路径数量,需要对Relax操作稍加修改,这里用n来代表每个结点的最短路径数量,每当结点发现一个新的最短路径,即v.d>u.d+w(u,v)时,更新v.n至u.n。当发现一个相同的最短路径,即v.d==u.d+w(u,v)时,有v.n=v.n+u.n,当然别忘了判断是否能增加最大救援队的数量。
5 7 0 4
10 20 30 40 50
0 1 1
1 3 1
3 4 1
0 4 3
0 2 1
2 3 1
0 3 2
上图的测试数据,输出为
4 130
0 2 3 4
#include<stdio.h>
#include<stdlib.h>
#include<ctype.h>
#define infinity 99999999
int V,E,S,T; //V 结点数 E 路径数 S 出发点 T 目的地
struct City
{
int number; //序号,输出路径时使用
int resuce_Member; //城市救援队数量
int total_Resuce_Member; //总救援队数量
int distance; //最短距离
bool alive; //结点状态
int routes_Number; //最短路径数量
City* parent; //最短路径的前驱节点,输出路径时使用
};
void Relax(City* u, City* v, int w) //w是从u至v的路径长度
{
if (v->distance > u->distance + w) //存在比当前路径更短的路径时
{
v->routes_Number = u->routes_Number; //将v结点最短路径数量更新至u结点的最短路径数量
v->distance = u->distance + w; //更新v结点的distance
v->total_Resuce_Member = u->total_Resuce_Member + v->resuce_Member; //更新救援队数量
v->parent = u; //更新前驱
}
else if (v->distance == u->distance + w) //路径相等时,增加v结点的最短路径数量,同时判断是否可以更新救援队数量
{
v->routes_Number += u->routes_Number;
if (v->total_Resuce_Member < u->total_Resuce_Member + v->resuce_Member)
{
v->total_Resuce_Member = u->total_Resuce_Member + v->resuce_Member;
v->parent = u;
}
}
}
void Print_Path(City* city)
{
if (city->parent == NULL)
{
printf("%d", city->number);
return;
}
Print_Path(city->parent);
printf(" %d", city->number);
}
int main()
{
scanf("%d %d %d %d", &V, &E, &S, &T);
City* city = (City*)malloc(sizeof(City) * V);
for (int i = 0; i < V; i++)
{
scanf("%d", &city[i].resuce_Member);
city[i].number = i;
city[i].routes_Number = 0;
city[i].alive = true;
city[i].total_Resuce_Member = 0;
city[i].distance = 0;
}
//使用二维数组来存储所有的路径,所有路径长度初始为-1,对于之后给出的路径,对其重新赋值
int** E_Routes_distance = (int**)malloc(sizeof(int*) * V);
for (int i = 0; i < V; i++)
E_Routes_distance[i] = (int*)malloc(sizeof(int) * V);
for (int i = 0; i < V; i++)
for (int j = 0; j < V; j++)
{
E_Routes_distance[i][j] = -1;
}
int s1, s2, length;
for (int i = 0; i < E; i++)
{
scanf("%d %d %d", &s1, &s2, &length);
E_Routes_distance[s1][s2] = length;
E_Routes_distance[s2][s1] = length;
}
for (int i = 0; i < V; i++)
{
city[i].distance = infinity;
}
//初始化出发点S
city[S].distance = 0;
city[S].parent = NULL;
city[S].routes_Number = 1;
city[S].total_Resuce_Member = city[S].resuce_Member;
while (true)
{
int minIndex = -1;
int minNumber = infinity;
for (int i = 0; i < V; i++) //找distance最小的未被移除结点
{
if (city[i].alive == true && city[i].distance < minNumber)
{
minIndex = i;
minNumber = city[i].distance;
}
}
if (minIndex == -1)break; //minNumber为-1,则说明所有结点已被移除,算法已终结
city[minIndex].alive = false; //alive被标记为false时,视该结点被移除
for (int i = 0; i < V; i++)
{
if (E_Routes_distance[minIndex][i] !=-1)
{
Relax(&city[minIndex], &city[i], E_Routes_distance[minIndex][i]);
}
}
}
printf("%d %d\n", city[T].routes_Number, city[T].total_Resuce_Member);
Print_Path(&city[T]);
free(city);
for (int i = 0; i < V; i++)
{
free(E_Routes_distance[i]);
}
free(E_Routes_distance);
}