题目链接:
传送门
题目描述:
作为一个城市的应急救援队伍的负责人,你有一张特殊的全国地图。在地图上显示有多个分散的城市和一些连接城市的快速道路。每个城市的救援队数量和每一条连接两个城市的快速道路长度都标在地图上。当其他城市有紧急求助电话给你的时候,你的任务是带领你的救援队尽快赶往事发地,同时,一路上召集尽可能多的救援队。
输入格式:
输入第一行给出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
样例输入图解:
编号为0的城市和编号为3的城市之间最短路径有2条,分别是0—>3,0—>1—>3。能够召集的最多的救援队数量为20+30+10=60。
救援路线为0—>1—>3。
本题主要设计到Dijkstra算法的相关思想。老样子,先上代码,然后再慢慢分析!
#include <stdio.h>
#include <stdlib.h>//malloc()函数的头文件。
#include <string.h>//memset()函数的头文件。
#define MAX 501//城市的个数N(2≤N≤500)。
#define INF 65535//宏定义无穷大。
int visited[MAX];//数组元素visited[i]记录编号为i的顶点是否被访问。0—未被访问,1—访问过了。
int dist[MAX];//数组元素dist[i]记录起始点S到编号为i的顶点的最短路径长度。
int cnt[MAX];//数组元素cnt[i]记录起始点S到编号为i的顶点的最短路径条数。
int ccf[MAX];//数组元素ccf[i]记录起始点S到达编号为i的城市时所召集的所有救援队数量 。
int pre[MAX];//数组元素pre[i]记录到达编号为i的城市前经过的城市编号。
/*图结构体的定义*/
typedef struct{
int vex[MAX];//顶点编号
int Num[MAX];//该顶点所拥有的救援队数目。
int edge[MAX][MAX];//邻接矩阵表示图。
int vertexnum,edgenum;//顶点数、边数。
}Graph,*gra;
/*创建图*/
gra createGraph(int n,int m)
{
gra G=(gra)malloc(sizeof(Graph));
G->vertexnum=n;
G->edgenum=m;
int i,j;
for(i=0;i<G->vertexnum;i++)
{
G->vex[i]=i;
G->Num[i]=0;//一开始将每个城市的救援队数目初始化为0。
}
for(i=0;i<G->vertexnum;i++)
{
for(j=0;j<G->vertexnum;j++)
G->edge[i][j]=INF;
}
return G;
}
void dijkstra(gra G,int start)
{
int i,j;
memset(visited,0,sizeof(visited));//初始化visited[]数组。
for(i=0;i<G->vertexnum;i++)
dist[i]=G->edge[start][i];
for(i=0;i<G->vertexnum-1;i++)
{
int min=INF,pos;
for(j=0;j<G->vertexnum;j++)
{
if(visited[j]==0&&dist[j]<min)
{
min=dist[j];
pos=j;
}
}
visited[pos]=1;
for(j=0;j<G->vertexnum;j++)
{
if(visited[j]==0&&dist[j]>dist[pos]+G->edge[pos][j])//到达某城市的最短路径。
{
dist[j]=dist[pos]+G->edge[pos][j];//最短路径更新。
pre[j]=pos;//记录上一个城市编号。
cnt[j]=cnt[pos];//拷贝到达上一个城市的最短路径条数。
ccf[j]=ccf[pos]+G->Num[j];//到达某城市召集的全部救援队数量。
}
else if(visited[j]==0&&dist[j]==dist[pos]+G->edge[pos][j])//发现其他最短路径。
{
cnt[j]=cnt[j]+cnt[pos];//更新到达当前城市时最短路径条数。
if(ccf[j]<ccf[pos]+G->Num[j])//最多救援队数量更新
{
pre[j]=pos;//记录上一个城市编号。
ccf[j]=ccf[pos]+G->Num[j];//更新救援队总数。
}
}
}
}
}
int main()
{
int N,M,S,D,i,t,v1,v2,d;
scanf("%d%d%d%d",&N,&M,&S,&D);
/*输入数据N(2≤N≤500)是城市的个数;M是快速道路的条数;
S是出发地的城市编号;D是目的地的城市编号。*/
gra G=createGraph(N,M);//创建图。
for(i=0;i<G->vertexnum;i++)
{
scanf("%d",&t);
G->Num[i]=t;//编号为i的城市的救援队数量。
ccf[i]=G->Num[i];
/*一开始将起始点S到编号为i的城市所召集的所有救援队数量初始化为
编号为i的城市所拥有的救援队数量。*/
cnt[i]=1; //起始点S到编号为i的顶点的最短路径条数初始化为1。
}
for(i=0;i<G->edgenum;i++)
{
scanf("%d%d%d",&v1,&v2,&d);//v1:城市1、v2:城市2、d:快速道路的长度。
G->edge[v1][v2]=G->edge[v2][v1]=d;//无向图。
}
dijkstra(G,S);
printf("%d %d\n",cnt[D],ccf[D]+G->Num[S]);
/*输出起始点S到终点D的最短路径条数,
输出所能召集的最多救援队数量。*/
/*第二行输出从S到D的路径中经过的城市编号。
数字间以空格分隔,输出结尾不能有多余空格。*/
int road[N];
int x=0;
int y=D;
while(pre[y]!=0)
{
road[x++]=pre[y];
y=pre[y];
}
printf("%d",S);
for(i=x-1;i>=0;i--)
printf(" %d",road[i]);
printf(" %d",D);
return 0;
}
这道题目是在Dijkstra算法的基础上附加了一些其他的条件,不再是一道单纯的模板题。总体而言,该题还是有一定难度的。我也是在借鉴了多方资源的基础上才搞出来的。
参考资料:
传送门
好了,本次的分享就先到这,如果大家有什么疑问或建议可以在评论区给我留言,感谢大家捧场!