Bootstrap

机试题——快乐校园跑

题目描述

国内大多数高校都会有一个活动,叫做校园跑。校园跑是一种在校园内进行的跑步运动,旨在提高学生的身体素质和团队合作能力。校园跑的规则是,每个参与者都要从自己所在的建筑物出发,沿着校园内的道路跑步,每到一个建筑他们需要到达这个建筑物的必经点,这样他们的手机会自动记录一次该建筑物。每个参与者都要尽可能多地经过必经点,同时也要尽可能快地完成跑步。

每个参与者的成绩由两个指标决定:经过的必经点个数和跑步总时间。经过的必经点个数越多,跑步总时间越短,成绩越好。校园跑不仅可以锻炼身体,还可以增加对校园各个建筑物的了解和认同感。

学校的这个活动,由很多个建筑物组成,每个建筑物都有一个编号,从1到 ( n )。一个学生从某个建筑物开始,沿着校园中的道路跑步。

由于学社联为了丰富这个活动的内容,让这个活动变得更好玩,他们会在一些建筑物的必经点安排一些小游戏,到达必经点后需要完成这些小游戏才能继续进行跑步。

但是仅一个人把学校所有的建筑物都跑完并完成小游戏,又会过于疲惫,于是学社联允许参加的学生们进行组队,以用时最长的那个人的成绩作为最终成绩。

从某个建筑物开始跑步,最多能经过多少个必经点,以及完成跑步的最快时间是多少。

我们已经知道了每个建筑物之间跑步的时间,以及每个必经点完成小游戏的时间,这些时间都用 runTimes 列表给出了。runTimes[i] 表示从建筑物 ( u ) 到建筑物 ( v ) 的时间是 runTime,如果 ( u ) 和 ( v ) 相同,就表示是这个必经点需要玩小游戏的时间。那么,从 ( u ) 到 ( v ) 的时间就是两者相加。现在给定一个起始建筑物 ( x ),请计算出完成跑步后能经过的最多必经点数和最终成绩时间。

输入描述

  • 第一行:一个正整数 ( n ),表示建筑物的个数,( 1 < n < 100 )。
  • 第二行:一个正整数 ( m ),表示路径的条数,( 1 < m < 10^4 )。
  • 接下来 ( m ) 行:每行包含三个正整数 ( u, v, runTime ),表示从 ( u ) 到 ( v ) 的跑步时间为 ( runTime ),但不代表 ( v ) 到 ( u ) 也是如此,( 1 < u, v, runTime < 100 )。
  • 最后一行:一个正整数 ( x ),表示校园跑的起点,( 1 < x < n )。

输出描述

输出为2行:

  • 第一行:最终可经过的必经点个数。
  • 第二行:塔子哥小队全部完成跑步后的具体时间。

用例输入

5
9
1 2 3
1 3 4
2 4 6
2 5 7
5 5 5
4 4 4
3 3 3
2 2 2
1 1 1
1
5
17

解释

  • 以建筑物1为起点计算小队最长耗时,分析可能会存在三条最大耗时路径,分别为1->3、1->2->5以及1->2->4。
  • 第一条路径1->3路径下,总耗时为4(1->3耗时)+3(3自身耗时)=7。
  • 第二条路径1->2->5路径下,总耗时为3(1->2耗时)+2(2自身耗时)+7(2->5耗时)+5(5自身耗时)=17。
  • 第三条路径1->2->4路径下,总耗时为3(1->2耗时)+2(2自身耗时)+6(2->4耗时)+4(4自身耗时)=15。
  • 所以在此场景下,小队完成跑步的最短时间为17分钟,为可以经过的所有必经点的最长时间。
  • 最终12345五个必经点都可以经过,所以最终输出结果如样例1输出。
5
6
1 2 10
1 3 20
2 4 30
3 4 40
4 5 50
5 1 60
3
5
160

解题思路

通过理解题意我们可以知道,由于组队不限人数,因此答案即为从起点出发,到达其它任一建筑物所需要花费的最长的时间(可以想象队里无数个人,每个人出发只去一个建筑物,最长的时间就是花费最多时间的那个人所耗时),而最多必经点数则为从起点出发能够到达的点。

解题步骤

  1. 读取输入数据

    • 读取建筑物数量 ( n ) 和路径数量 ( m )。
    • 读取每条路径的起点 ( u )、终点 ( v ) 和跑步时间 ( t )。如果 ( u ) 和 ( v ) 相同,则表示这是必经点的小游戏时间。
    • 读取起点 ( be )。
  2. 初始化数据结构

    • 使用邻接表 g 存储路径信息,其中 g[u] 存储从建筑物 ( u ) 到其他建筑物的路径及其时间。
    • 使用数组 time 存储每个必经点的小游戏时间。
    • 使用数组 dp 存储从起点到每个建筑物的最短耗时,初始值为 -1(表示未计算)。
    • 使用队列 q 进行广度优先搜索(BFS)。
  3. 处理路径和必经点时间

    • 对于每条路径,如果起点和终点不同,则将路径信息存储到邻接表中。
    • 如果起点和终点相同,则将时间存储到 time 数组中。
    • 对于邻接表中的每条路径,加上终点的必经点时间。
  4. 广度优先搜索(BFS)

    • 从起点开始,初始化 dp[be] 为 0,并将起点加入队列。
    • 遍历队列,对于当前建筑物 ( cur ) 和当前耗时 ( t ),遍历其所有邻接点 ( nx ) 和对应的耗时 ( nt )。
    • 如果 dp[nx] 未计算过或当前路径耗时更短,则更新 dp[nx],并将 ( nx ) 和新的耗时 ( t + nt ) 加入队列。
  5. 计算结果

    • 遍历 dp 数组,统计可以到达的必经点数量 ( num ) 和最长耗时 ( res )。
    • 如果 dp[i] 不为 -1,则表示可以到达该必经点,更新 ( num ) 和 ( res )。

代码

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<map>
#include<algorithm>
#include<string>
#include<vector>
#include<unordered_map>
#include<unordered_set>
#include<queue>
#include<set>
#include<list>
#include <sstream>
#include <bitset>
#include <stack>
using namespace std;
int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    // 不限制人数
    // 对于所有点求最短路 然后加上自己的时间(如果是必经点) 求最大值
    vector<vector<int> > g[105];
    vector<int> time(105,0);//必经点的时间
    int n,m;
    cin >> n>>m;
    for (int i = 0; i < m; i++) {
        int u, v, t;
        cin >> u >> v >> t;
        if (u != v) {
            g[u].push_back({ v,t });
        }
        else {
            time[u] = t;
        }
    }
    // 需要在路上加上这些必经点小游戏时间(加上目的地的)
    for (int i =1; i <=n; i++) {
        for (int j = 0; j < g[i].size(); j++) {
            int target = g[i][j][0];
            g[i][j][1] += time[target];
        }
    }
    int be;
    cin >> be;
    vector<int> dp(n+1, -1);
    dp[be] = 0;
    queue<vector<int>> q;
    q.push({ be,0});
    while (!q.empty()) {
        int cur = q.front()[0];
        int t= q.front()[1];
        q.pop();
        for (int i = 0; i < g[cur].size(); i++) {
            int nx = g[cur][i][0]; //下一个点
            int nt= g[cur][i][1]; // 时间
            if (dp[nx] == -1 || t + nt < dp[nx]) {
                // 没计算过或者更短
                dp[nx] = t + nt;
                q.push({ nx,t + nt });
            }
        }
    }
    // 找最大值
    int num = 0;// 可以过几个必经点?
    int res = 0;
    for (int i = 1; i <=n; i++) {
        //cout << dp[i] << " ";
        if (dp[i] != -1) num++;
        res = max(res, dp[i]);
    }
    cout << num<<"\n" << res;
}
;