L2-001 紧急救援 天梯赛(25 分C++)

作为一个城市的应急救援队伍的负责人,你有一张特殊的全国地图。在地图上显示有多个分散的城市和一些连接城市的快速道路。每个城市的救援队数量和每一条连接两个城市的快速道路长度都标在地图上。当其他城市有紧急求助电话给你的时候,你的任务是带领你的救援队尽快赶往事发地,同时,一路上召集尽可能多的救援队。
输入格式:

输入第一行给出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

思路:刚开始选择dfs搜索所有路径,尽管有剪枝但最后一个测试点是一定会超时的,所以这里选择dijkstra算法求从起点开始到每个顶点的最短路和最多人数,值得注意的是,题目要求在保证最短路的前提下尽可能叫到最多的人,并且输出最短路的路径数,因此我们得先找到最短路,在这个基础上再找叫到最多的人的路径。
用P存储路径,cost存储起点到每个城市的最多人数,count1存储从起点到每个城市的最短路径数。
注意:
1.count1在计算的时候需要继承之前的路径数
2.在更新时尽管这条路叫的人数比之前的少,但是只要比之前的最短路短,无论人数多少我们都要更新路径,因为救人的时候最短路是前提

#include <bits/stdc++.h>
using namespace std;
#define inf 0x3f3f3f3f
int a[501],G[501][501],M,N,S,D,sum1,book[501];
int P[501],dis[501],cost[501],count1[501];//P存储该点的前驱节点 dis存储每个点的最短距离 cost存储最大数量 count1存储最短路径条数
void print(int E) //递归打印路径 传入终点
{     if(E==S) //当遇到起始点时
      { printf("%d",E);
          return ;
      }
     else
     {    print(P[E]);
          printf(" %d",E);
     }
}
void dijk(int start)//dijkstra算法求最短路
{
     book[start]=1;
     cost[start]=a[start];
     for(int i=0;i<N;i++)
      { if(G[start][i]!=inf)
          { dis[i]=G[start][i]; //初始化dis p cost count1
            P[i]=start;
          cost[i]=cost[start]+a[i];
          count1[i]=1;
          }
      }
     for(int i=0;i<N-1;i++)
     {
          int min1=inf,k=0;
          for(int j=0;j<N;j++)
          {
               if(!book[j]&&min1>dis[j]) //找最短路
               {
                    min1=dis[j];
                    k=j;
               }
          }
          book[k]=1;//标记
          for(int j=0;j<N;j++)
          {
               if(!book[j]&&min1+G[k][j]<=dis[j])//松弛
                {
                     if(min1+G[k][j]==dis[j]) //如果最短距离相等
                      { count1[j]+=count1[k]; //路径要继承之前的+当前的
                        if(cost[k]+a[j]>cost[j])//如果召集的数量更多
                          {   P[j]=k;    //松弛路径
                              cost[j]=cost[k]+a[j]; //松弛数量
                          }
                      }
                     else  //如果比之前的最短距离小
                     { count1[j]=count1[k];//继承之前路径
                       dis[j]=min1+G[k][j];//最短距离松弛
                       P[j]=k;  //无论召集人数多少都要松弛
                       cost[j]=cost[k]+a[j];
                     }   
                }
          }
     }
     cout<<count1[D]<<" "<<cost[D]<<endl;
     print(D);
}
int main()
{
     //freopen("in.txt","r",stdin);
     scanf("%d%d%d%d",&N,&M,&S,&D);
     memset(dis,inf,sizeof(dis));
     memset(G,inf,sizeof(G));
     for(int i=0;i<N;i++) //输入每个城市的人数
       scanf("%d",&a[i]);
     for(int i=0;i<M;i++) //构建图
     {
          int a1,b1,w;
          scanf("%d%d%d",&a1,&b1,&w);
          G[a1][b1]=G[b1][a1]=w;
     }
    dijk(S);
     return 0;
}
  • 4
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

乔梦圆的博客

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值