[PAT解题报告] Emergency

题目给了一个图,代表若干个城市,每个城市里有若干人,城市靠道路相连——总体来说是一个无向图,每个节点有一个权值,边也有权值,代表路径长度。然后给定一个起点,是你所在的城市,再给定一个终点——表示目的城市,一旦遇到紧急情况,你需要以最快的速度把尽可能多的人放到目的城市。求两个值 (1) 从起点到终点有多少条最短路径 (2) 走最短路的前提下,你最多能运送多少人到终点?

分析: 这是一个典型的最短路问题。后面还会看到其实PAT最短路的问题还挺多的,但都不是直接求最短路那么简单。比如本题,还要求最短路的长度。我们考虑一下Dijkstra算法的核心步骤:
if (d[i] > d[j] + w(j, i)) then d[i] = d[j] + w(j, i)
其中j是我们目前还没有用过的距离最小的点, w(j, i)表示j到i的距离,我们会用d[j] + w(j, i)更新d[i]的最小距离。
如何计算最短路的条数呢? 玄机也在这里!我们用数组way[x]表示从起点到x的最短路条数。
如果d[i] == d[j] + w(j, i) 原来达到j的最短路加上边(j,i)也是达到j的最短路,于是累加way[i] += way[j]。
如果d[i] > d[j] + w(j, i) 我们更新d[i] = d[j] + w(j, i)的同时,得到了更从起点到j更短的路,所以之前的计算的条数没有意义了,直接有way[i] = way[j]
那么怎样计算最多运送的人数呢?
也是类似的。我们用num[x]表示从起点到达x走最短路能运送的最多的人数。并且b[x]表示在x处有多少人。
如果d[i] == d[j] + w(j, i),  num[i] = max(num[i], num[j] + b[i]) 这是因为我们选择是否从j走到i要看经过j是不是比我们现在带的人多。
如果d[i] > d[j] + w(j, i), 更新d[i] = d[j] + w(j, i)的同时,我们只能用num[i] = num[j] + b[i],因为我们一定要优先走最短路——带的人数是第二关键字,所以我们别无选择,只能选择经过j。
因此,本题关键在于理解dijkstra算法更新的过程,以及最短路的形成——其实本质就是s到i的最短路,包含了s到j的最短路,加上j到i的那条边的长度——一切更新都源于此。

注意: 我假设了两个点间的最短边只有一条,如果有平行边(两点间最短的边有多个),要记录一下最短边的条数,更新的时候
 way[i] = way[k] * edge_count[i, k]
或者way[i] += way[k] * edge_count[i, k]
edge_couint [i, k] 表示i,k之间有多少条边都是最短长度的…… 邻接矩阵要多存一个值。

代码:
#include <cstdio>
#include <cstring>
#include <string>
#include <algorithm>

using namespace std;

const int inf = 1000000000;
const int N = 502;

int n;
bool mark[N];
int a[N][N];
int num[N];
int b[N];
int cost[N];
int way[N];

void dijkstra(int s) {
  for (int i = 0; i < n; ++i) {
    cost[i] = inf;
    mark[i] = false;
    num[i] = way[i] = 0;
  }
  num[s] = b[s];
  way[s] = 1;
  cost[s] =  0;
  for (int j = 0; j < n; ++j) {
    int k = -1;
    for (int i = 0; i < n; ++i) {
      if ((!mark[i]) && ((k < 0) || (cost[i] < cost[k]))) {
        k = i;
      }
    }
    mark[k] = true;
    for (int i = 0; i < n; ++i) {
      if ((!mark[i]) && (a[k][i] < inf)) {
        int temp = cost[k] + a[k][i];
        if (temp < cost[i]) {
          cost[i] = temp;
          num[i] = num[k] + b[i];
          way[i] = way[k];
        }
        else if (temp == cost[i]) {
          num[i] = max(num[k] + b[i], num[i]);
          way[i] += way[k];
        }
      }
    }
  }
}


int main() {
int m,from,to;
  scanf("%d%d%d%d",&n,&m,&from,&to);
  for (int i = 0; i < n; ++i) {
    scanf("%d",b + i);
    for (int j = 0; j < n; ++j) {
      a[i][j] = inf;
    }
  }
  for (;m;--m) {
    int x,y,z;
    scanf("%d%d%d",&x,&y,&z);
    a[x][y] = a[y][x] = min(a[x][y],z);
  }
  dijkstra(from);
  printf("%d %d\n",way[to], num[to]);
  return 0;
}


原题链接: http://www.patest.cn/contests/pat-a-practise/1003
全部评论
错了,虽然提交了accepted。试试这组数据 6 10 0 2 1 1 1 2 2 3 0 1 1 1 3 2 0 3 1 0 4 2 0 5 3 0 2 4 3 2 3 2 5 1 4 2 2 3 4 1
点赞 回复 分享
发布于 2020-02-01 14:55

相关推荐

10-21 00:37
已编辑
门头沟学院 C++
小浪_Coding:你问别人,本来就是有求于人,别人肯定没有义务免费回答你丫, 有点流量每天私信可能都十几,几十条的,大家都有工作和自己的事情, 付费也是正常的, 就像你请别人搭把手, 总得给人家买瓶水喝吧
点赞 评论 收藏
分享
bg27强双非本,目前在学习golang后端gin框架部分,在b站找了一个轮子项目敲了一下,技术栈是gin&nbsp;+&nbsp;gorm&nbsp;+&nbsp;mysql&nbsp;+&nbsp;redis。我目前的想法是这一个月学习408和go八股以及刷算法然后在12月找个寒假实习然后大三下开始准备考研。我是考研意愿比较强烈,想问一下我是应该all&nbsp;in其中一个方向吗,我感觉我实习对我考研来说也是没什么帮助的好像。
牛客28967172...:毕业工作,考研,考公是完全不同的方向。 99%的人拼尽全力也只能把一个做好(能做好都已经是佼佼者了,比如进进大厂,考985或者考公) 如果你确定要考研可以不用学任何就业技术框架,也不用实习经验,刷题背知识点就行,但注意必须考92院校起步,因为这个年代双非硕毕业后完全不如双非本(互联网行业),可以说双非硕在互联网就业完全是负收益
投递哔哩哔哩等公司10个岗位
点赞 评论 收藏
分享
评论
点赞
收藏
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务