菜鸟记录PAT甲级1003--Emergency

Posted whf10000010

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了菜鸟记录PAT甲级1003--Emergency相关的知识,希望对你有一定的参考价值。

  久违的PAT,由于考研408数据结构中有一定需要,同时也是对先前所遗留的竞赛遗憾进行一定弥补 ,再次继续PAT甲级1003.。

As an emergency rescue team leader of a city, you are given a special map of your country. The map shows several scattered cities connected by some roads. Amount of rescue teams in each city and the length of each road between any pair of cities are marked on the map. When there is an emergency call to you from some other city, your job is to lead your men to the place as quickly as possible, and at the mean time, call up as many hands on the way as possible.

Input Specification:

Each input file contains one test case. For each test case, the first line contains 4 positive integers: N (500) - the number of cities (and the cities are numbered from 0 to N1), M - the number of roads, C1 and C2 - the cities that you are currently in and that you must save, respectively. The next line contains N integers, where the i-th integer is the number of rescue teams in the i-th city. Then M lines follow, each describes a road with three integers c1c2 and L, which are the pair of cities connected by a road and the length of that road, respectively. It is guaranteed that there exists at least one path from C1 to C2.

Output Specification:

For each test case, print in one line two numbers: the number of different shortest paths between C1 and C2, and the maximum amount of rescue teams you can possibly gather. All the numbers in a line must be separated by exactly one space, and there is no extra space allowed at the end of a line.

Sample Input:

5 6 0 2
1 2 1 5 3
0 1 1
0 2 2
0 3 1
1 2 1
2 4 1
3 4 1
 

Sample Output:

2 4

   就是说一个救援队,在地图所给N个的城市里进行救援,每个城市之间有M条路进行连接且有一定数量的救援人员,C1是初始地点,C2是目的地。

  所需输入第一行是:城市数量、路径数量、初始地、目的地

      第二行是:各城市所包含的救援人数(共N个数据)

      接下来的M行:地点1、地点2、1,2间的距离L

  所得输出:最短路径的数量、最多得到的救援人数

题目分析

    一眼图的遍历,那么能用到的方法很多,例如广度优先、深度优先、迪杰斯特拉等等,笔者在此用深度优先,迪杰斯特拉等“高级”算法往后更新。

    要清楚整体大概思路:首先对数值进行输入,对于数组,初始化应是尽可能大还是尽可能小?然后深度遍历各节点时,如何遍历下去的条件是什么?如何选择路径,到达目的地之前应该怎么进入下一个节点?达到目的地后,如何判断是最小路径?如何记录和比较最多救援人数?

个人想法

    那么首先对于变量的设置

 int N, M, C1, C2;//题目所给城市数量、路径数量、初始地、目的地
 int c1, c2, l, dis[501][501];//二维数组dis用于记录我们所输入的M行中地点1和地点2之间的距离l3 int paths, teams;//输出的两个结果:路径数,人员数
4 int mindis[501];//计算过程中记录某条路劲上的当前最短路径
5 int* cteam;//各城市的救援人数,这里其实是个数组,写成指针是为了方便在主函数中进行内存管理malloc和初始化memset

    我在此均设为全局变量,因为在后续的编写中我发现会单独写出一个dfs函数,而用全局变量可以更容易调用。要清楚设为数组的条件是记录多条数据,再次如城市是否连接,各城市间的路径长度,各城市的救援人数,遍历每条路径所需要的各路径总长度(涉及比较和有下标组成的路径标识,所以需要数组记录,单纯的变量无法做到),其次对于函数的中间变量我采取即写即设,并没有在开始就尽可能将其完全想到。

    然后编写数据的输入和输出。

1 //初始化dis数组,不相连的无穷大距离,自身0距离or-1?,表示距离的同时表示有无连接 
2 for (int i = 0; i < 501; i++) 
3 for (int j = 0; j < 501; j++)
 4     if (i == j)dis[i][j] = 0;//自身距离
 5        else
 6         dis[i][j] = 9999999;//设为无穷大
 7    
 8     scanf("%d%d%d%d", &N, &M, &C1, &C2);

 9     cteam = malloc(sizeof(int) * 4);
10     memset(cteam, 0, sizeof(cteam) * 4);//记录每个城市的team数,初始为0个救援人员
11     for (int i = 0; i < N; i++) 
12         scanf("%d", &cteam[i]);  
13     
14     for (int i = 0; i < M; i++) 
15         scanf("%d%d%d", &c1, &c2, &l);
16         dis[c1][c2] = l;    
17         dis[c2][c1] = l;    //无向图,c1->c2==c2->c1,所以两个距离相等
18     
19     for (int i = 0; i < 501; i++)mindis[i] = 9999999;  //需要注意的是这里mindis用于存放某条路径的长度,设为一个无穷大的是为了在后续比较中让我们所输入的“非无穷大”的距离记录并比较
                                   //换句话说,如果这里初始为0,那么往后输入、记录的每条有效路径的长度都会大于0,从而导致最短路径无法更新

20     dfs(C1, 0, cteam[C1]);//深度遍历函数
21     printf("%d %d", paths, teams);

 

    接下来就是dfs函数的编写,在次先回答分析阶段的几个问题。

  深度遍历各节点时,如何遍历下去的条件是什么?

  条件就是我们的答案,即最短路径(这里没加上最多人数是因为得到最多人数的前提是所得的最短路径存在),所以应该满足当前所走的路径curlen小于目前为止该地的最短路径mindis[curcity],到这其实也发现mindis数组不仅记录到该节点有无被访问,也记录历来被访问时所经历的最短路径!所以如果大于mindis,那么说明这条路径肯定不是最短,可以直接返回到上一个路径并重新选择路线;反之就继续遍历。

  如何选择路径,到达目的地之前应该怎么进入下一个节点?

   笔者个人对于这种所需函数相同,且需要记录的题目总是会想到迭代,大部分时候都是管用的。所以在此就是不断迭代,每次迭代下一个位置的节点、目前所走长度curlen+当前与下一个节点的距离dis、目前所有人员数+下一个节点所有人员数。

  达到目的地后,如何判断是最小路径?如何记录和比较最多救援人数?

   该路径目前的长度curlen是否与目标地的mindis相同,(第一次遍历的时候应是不同)不同时,将path归为1,记录当前team人数,并将此时的curlen视为最短路径对目标地的mindis赋值;相同时,path++,比较并记录最新的最多人数。这里指出  必须分为不同或相同的情况,不可以大于或小于 --> path在此时总是归为1,因为如果大于mindis只会存在第一次到达目的地时mindis初始无穷大的状态,后续在抵达,如果有比第一次到达路径长的情况会在往次迭代被终止;如果小于,那么最短路径和数量就会更新,path归为1。

 void dfs(int curcity, int curlen, int curteam) //每次传入节点,路径长,队伍人员
     if (curlen > mindis[curcity])return;      //如果比该节点所记录的最短路径要短,直接退出
     if (curcity == C2)                 //到达目的地,并判断是否是最短路径
         if (curlen != mindis[curcity])       //是最新的最短路径
             paths = 1;
             mindis[C2] = curlen;
             teams = curteam;
         
         else                       //相同的最短路径
             paths++;
             if (curteam > teams)teams = curteam;
         
     
     else
     
         if (curlen < mindis[curcity])mindis[curcity] = curlen;    //不大于当前最短路径时,更新最短节点  
         for (int i = 0; i < 501; i++) 
             if (dis[curcity][i] != 9999999 && i != curcity)      //遍历每个节点,同时选择有效路径进行迭代
                 dfs(i, curlen + dis[curcity][i], curteam + cteam[i]);//叠加路径长度和人员数量
         
     
 

 

 

<-----------------------------------完整代码----------------------------------->

 

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
int N, M, C1, C2;
int c1, c2, dis[501][501];
int l;
int paths, teams;
int mindis[501];
int* cteam;
void dfs(int curcity, int curlen, int curteam) 
    if (curlen > mindis[curcity])return;
    if (curcity == C2) 
        if (curlen != mindis[curcity]) 
            paths = 1;
            mindis[C2] = curlen;
            teams = curteam;
        
        else 
            paths++;
            if (curteam > teams)teams = curteam;
        
    
    else
    
        if (curlen < mindis[curcity])mindis[curcity] = curlen;
        for (int i = 0; i < 501; i++) 
            if (dis[curcity][i] != 9999999 && i != curcity)
                dfs(i, curlen + dis[curcity][i], curteam + cteam[i]);
        
    


int main() 
    //初始化dis数组,不相连的无穷大距离,自身0距离or-1?
    for (int i = 0; i < 501; i++)
        for (int j = 0; j < 501; j++)
            if (i == j)dis[i][j] = 0;
            else
                dis[i][j] = 9999999;//设为无穷大
   
    scanf("%d%d%d%d", &N, &M, &C1, &C2);
    cteam = malloc(sizeof(int) * 4);
    memset(cteam, 0, sizeof(cteam) * 4);//记录每个城市的team数
    for (int i = 0; i < N; i++) 
        scanf("%d", &cteam[i]);
    
    for (int i = 0; i < M; i++) 
        scanf("%d%d%d", &c1, &c2, &l);
        dis[c1][c2] = l;
        dis[c2][c1] = l;
    
    for (int i = 0; i < 501; i++)mindis[i] = 9999999;
    dfs(C1, 0, cteam[C1]);
    printf("%d %d", paths, teams);
    return 0;

最后最后

 

 我自己的VS2022多次实验是没有问题的,但是PAT就。。。。

 

多次调试甚至结果都没有,只是提醒scanf出现return问题,反复看了很多次无解,就很无奈。。。在此还是希望各位看官可以帮我找找问题并指正,不胜感激!

 

 

 

总结

    第三题从某种意义来说是真正需要思考的题目,考的虽然是图,但还是很简单的一种,对于此题,无它,看懂图,理清每条思路即可,有必要说的是,对于竞赛也好,Leecode和洛也好,c的问题貌似总是大于c++的,也不知道现在改c++还能不能来的及。。。

    感谢您能看到这,菜鸟记录,挑战每种错误的极限!

 

甲级1003 Emergency

1003 Emergency (25 分)

As an emergency rescue team leader of a city, you are given a special map of your country. The map shows several scattered cities connected by some roads. Amount of rescue teams in each city and the length of each road between any pair of cities are marked on the map. When there is an emergency call to you from some other city, your job is to lead your men to the place as quickly as possible, and at the mean time, call up as many hands on the way as possible.

Input Specification:

Each input file contains one test case. For each test case, the first line contains 4 positive integers: N (500) - the number of cities (and the cities are numbered from 0 to N?1), M - the number of roads, C?1?? and C?2?? - the cities that you are currently in and that you must save, respectively. The next line contains Nintegers, where the i-th integer is the number of rescue teams in the i-th city. Then M lines follow, each describes a road with three integers c?1??, c?2?? and L, which are the pair of cities connected by a road and the length of that road, respectively. It is guaranteed that there exists at least one path from C?1?? to C?2??.

Output Specification:

For each test case, print in one line two numbers: the number of different shortest paths between C?1?? and C?2??, and the maximum amount of rescue teams you can possibly gather. All the numbers in a line must be separated by exactly one space, and there is no extra space allowed at the end of a line.

Sample Input:

5 6 0 2
1 2 1 5 3
0 1 1
0 2 2
0 3 1
1 2 1
2 4 1
3 4 1

Sample Output:

2 4



题意:有几座城市,每座城市中有救援的人手,然后救援大队长从一座城市出发,到另一座城市,找到最短的路共有几条,可带的最多的救援人手有多少(每经过一个城市,就可以把这个城市的人手带走,相当于累加)

分析:递归DFS,当遇到终点时即返回。注意最后输出的是最短的路有几条和可携带的最多救援人手。(WA就是因为输出成最短路径长度了)
在写的过程中还有一个地方也当时没能一次写对:在求最多救援人手的时候,条件应该是
  1. 如果当前已经过的距离和已知的最短距离相等时,则如果当前人手大于已知的最多救援人手,就刷新最多救援人手。

  2. 若当前经过的距离比已知的最短距离更短时,直接刷新最短距离和最大救援人手。代码如下:
 1 #include <iostream>
 2 #include <cstring>
 3 using namespace std;
 4 
 5 int N, M;                        //城市数,道路数
 6 int outset, des;
 7 int minLen=-1, maxRes=-1, roadCount;    //最短距离,最大救援数量,道路计数
 8 int currentLen, currentWeight;    //当前累计长度,当前累计权重
 9 int cmap[505][505];
10 int city_rescue[505];
11 bool vis[505];                    //判断该城市是否被访问过
12 
13 void init()
14 {
15     for (int i = 0; i < 505; i++)
16         for (int j = 0; j < 505; j++)
17             cmap[i][j] = -1;        //初始化各城市之间没有道路
18 }
19 void DFS(int begin)
20 {
21     currentWeight += city_rescue[begin];    //加上该起点城市的权重
22     if (begin == des)        //如果已经访问至终点
23     {
24         if (minLen == -1 || currentLen < minLen)
25         {
26             minLen = currentLen;
27             maxRes = currentWeight;
28             roadCount = 1;
29         }
30         else if (currentLen == minLen)
31         {
32             roadCount++;
33             if(currentWeight>maxRes)
34                 maxRes = currentWeight;
35         }
36             
37         currentWeight -= city_rescue[begin];
38         return;
39     }
40 
41     vis[begin] = true;
42     for (int i = 0; i < N; i++)
43     {
44         if (cmap[begin][i] != -1 && vis[i] == false)    //与起点城市有路且未被访问
45         {
46             currentLen += cmap[begin][i];        //访问该城市前,路径长度先加上
47             DFS(i);        //递归访问i城市
48             currentLen -= cmap[begin][i];
49         }
50     }
51     currentWeight -= city_rescue[begin];
52     vis[begin] = false;
53 }
54 int main()
55 {
56     init();
57     scanf("%d %d %d %d", &N, &M, &outset, &des);
58     for (int i = 0; i < N; i++)
59         scanf("%d", &city_rescue[i]);
60     for (int i = 0; i < M; i++)
61     {
62         int a, b, weight;
63         scanf("%d %d %d", &a, &b, &weight);
64         cmap[a][b] = cmap[b][a] = weight;
65     }
66     DFS(outset);
67     printf("%d %d", roadCount, maxRes);
68     return 0;
69 }

 

贴一下DFS伪代码

1 DFS(u) {
2     vis[u] = true;
3     for (从u出发能达到的所有顶点v)
4         if vis[v] == false
5             DFS(v);    //递归访问
6 }

 








以上是关于菜鸟记录PAT甲级1003--Emergency的主要内容,如果未能解决你的问题,请参考以下文章

PAT 甲级 1003. Emergency (25)

PAT 甲级 1003 Emergency

(做题目学英语)PAT甲级第三题--1003 Emergency

PAT甲级1003

甲级1003 Emergency

PAT-1003 Emergency