【备战秋招】每日一题:2023.05.10-华为OD机试(第三题)-快乐校园跑

文章介绍了校园跑活动,参与者需经过多个必经点并尽可能快地完成。通过Floyd算法,计算从起点出发能经过的最多必经点数及完成跑步的最快时间。算法考虑了路径时间和小游戏时间,组队不限人数,以最长耗时为准。样例展示了算法的应用,并提供了不同编程语言的实现代码示例。
摘要由CSDN通过智能技术生成

为了更好的阅读体检,可以查看我的算法学习博客
在线评测链接:P1308

题目描述

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

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

塔子哥也参加了他的学校的这个活动,由很多个建筑物组成,每个建筑物都有一个编号,从1到 n n n 。一个学生从某个建筑物开始,沿着校园中的道路跑步。

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

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

塔子哥和他的同学组成了一支小队(不限人数),他们想知道,从某个建筑物开始跑步,最多能经过多少个必经点,以及完成跑步的最快时间是多少。

我们已经知道了每个建筑物之间跑步的时间,以及每个必经点完成小游戏的时间,这些时间都用 r u n T i m e s runTimes runTimes 列表给出了。

r u n T i m e s [ i ] runTimes[i] runTimes[i] 表示从建筑物 u u u v v v 建筑物的时间是 r u n T i m e runTime runTime ,如果 u u u v v v 相同,就表示是这个必经点需要玩小游戏的时间。那么,从 u u u v v v 的时间就是两者相加。现在给定一个起始建筑物 x x x,请计算出完成跑步后能经过的最多必经点数和最终成绩时间。

输入描述

输入第一行为一个正整数 n n n ,表示建筑物的个数.( 1 ≤ n ≤ 100 1 \leq n \leq 100 1n100)

输入第二行为一个正整数 m m m ,表示路径的条数.( 1 ≤ m ≤ 1 0 4 1 \leq m \leq 10^4 1m104)

接下来 m 行输入每行为三个正整数 u , v , r u n T i m e u,v,runTime u,v,runTime,表示 r u n T i m e s [ i ] runTimes[i] runTimes[i] 的三个元素,即从 u->v 需要的时间为 r u n T i m e runTime runTime,但不代表 v->u 也是如此.( 1 ≤ u , v , r u n T i m e ≤ 100 1 \leq u,v,runTime \leq 100 1u,v,runTime100)

最后一行输出校园跑的起点 x x x.( 1 ≤ x ≤ n 1 \leq x \leq n 1xn)

输出描述

输出为2行。第一行输出为最终可经过的的必经点个数,第二行输出这些塔子哥小队全部完成跑步后的具体时间。

样例1

样例输入

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。

​ 所以在此场景下,小队完成跑步的最短时间为38分钟,为可以经过的所有必经点的最长时间。

​ 最终输出结果分两行输出,最终12345五个必经点都可以经过,所以最终输出结果如样例1输出。

样例2

样例输入

5
6
1 2 10
1 3 20
2 4 30
3 4 40
4 5 50
5 1 60
3

样例输出

5
160

思路:floyd最短路

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

floyd算法:

定义 f [ i ] [ j ] f[i][j] f[i][j] 为从 i i i 出发到达 j j j 点所需要花费的时间。

直观理解上,我们从 i i i j j j ,无非就是两个选择;

  1. i i i j j j 有直达的路,所以直接从 i i i 前往 j j j
  2. 通过另一个点 k k k 作为中继,先去 k k k ,再从 k k k j j j

于是其算法方程就出来了: f [ i ] [ j ] = m i n ( f [ i ] [ j ] , f [ i ] [ k ] + f [ k ] [ j ] ) f[i][j]=min(f[i][j],f[i][k]+f[k][j]) f[i][j]=min(f[i][j],f[i][k]+f[k][j])

直观理解就是,在两个路线的方案选择中,选择耗时较少的方案。

于是为了求出任意 f [ i ] [ j ] f[i][j] f[i][j] ,我们需要枚举 k , i , j k,i,j k,i,j 三个变量,每个都可以从 N N N 个点中选择其一,所以其时间复杂度为 O ( N 3 ) O(N^3) O(N3),一般看到数据量为100左右,就很有可能是该算法。

在本题中,需要稍稍做点修改:

其中, a [ i ] a[i] a[i] 为在 i i i 点做游戏所需要的时间。可以就着原理稍微理解一下。

if(k==j){// 中继点和终点相同,相当于直接从i前往j 
    f[i][j]=min(f[i][j],f[i][k]+a[k]);
}else{// 中继点和终点不同,通过中继点更新 
    f[i][j]=min(f[i][j],f[i][k]+f[k][j]);
}

类似题目推荐

代码

C++

#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;

int n,m;
int Map[105][105];
int f[105][105];
int a[105];
int s;

int main(){
	scanf("%d %d",&n,&m);
	// floyd算法
	// 初始化 
	// 初始化为极大值 
	memset(f,0x3f,sizeof(f));
	for(int i=1,x,y,t;i<=m;++i){
		scanf("%d %d %d",&x,&y,&t);
		if(x!=y) f[x][y]=t;
		else a[x]=t;
	}
	// 更新从i到j所需要时间加上玩游戏的时间 
	for(int i=1;i<=n;++i){
		for(int j=1;j<=n;++j){
			if(i!=j) f[i][j]+=a[j];
		}
	} 
	for(int i=1;i<=n;++i) f[i][i]=0;// 由于玩游戏时间已经加在路程时间上了,因此从i到i时间应该为0 
	// 求解 
	for(int k=1;k<=n;++k){
		for(int i=1;i<=n;++i){
			for(int j=1;j<=n;++j){
				if(i==j) continue;
				if(k==j){// 中继点和终点相同,相当于直接从i前往j 
					f[i][j]=min(f[i][j],f[i][k]+a[k]);
				}else{// 中继点和终点不同,通过中继点更新 
					f[i][j]=min(f[i][j],f[i][k]+f[k][j]);
				}
			}
		}
	}
	scanf("%d",&s);
	int sum=0,maxx=0;
	for(int i=1;i<=n;++i){
		if(i==s) continue;
		if(f[s][i]<0x3f3f3f3f){
			sum++;
			maxx=max(maxx,f[s][i]);
		}
	}
	printf("%d\n%d",sum+1,maxx);// 别忘记答案加上起点 
	
	return 0;
}

python

import sys

n, m = map(int, sys.stdin.readline().split())
Map = [[float('inf')] * 105 for _ in range(105)]
f = [[float('inf')] * 105 for _ in range(105)]
a = [0] * 105
s = 0

for i in range(1, m + 1):
    x, y, t = map(int, sys.stdin.readline().split())
    if x != y:
        Map[x][y] = t
    else:
        a[x] = t

for i in range(1, n + 1):
    for j in range(1, n + 1):
        if i != j:
            Map[i][j] += a[j]  # 更新从i到j所需要时间加上玩游戏的时间

for i in range(1, n + 1):
    f[i][i] = 0  # 由于玩游戏时间已经加在路程时间上了,因此从i到i时间应该为0

# 求解
for k in range(1, n + 1):
    for i in range(1, n + 1):
        for j in range(1, n + 1):
            if i == j:
                continue
            if k == j:  # 中继点和终点相同,相当于直接从i前往j
                f[i][j] = min(f[i][j], f[i][k] + a[k])
            else:  # 中继点和终点不同,通过中继点更新
                f[i][j] = min(f[i][j], f[i][k] + f[k][j])

s = int(sys.stdin.readline())
sum = 0
maxx = 0

for i in range(1, n + 1):
    if i == s:
        continue
    if f[s][i] < float('inf'):
        sum += 1
        maxx = max(maxx, f[s][i])

print(sum + 1, maxx)

Java

import java.util.Scanner;

public class Main {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        int n = scanner.nextInt();
        int m = scanner.nextInt();
        int[][] Map = new int[105][105];
        int[][] f = new int[105][105];
        int[] a = new int[105];
        int s;

        // floyd算法
        // 初始化
        // 初始化为极大值
        for (int i = 1; i <= n; i++) {
            for (int j = 1; j <= n; j++) {
                f[i][j] = Integer.MAX_VALUE / 2;
            }
        }

        for (int i = 1; i <= m; i++) {
            int x = scanner.nextInt();
            int y = scanner.nextInt();
            int t = scanner.nextInt();
            if (x != y) {
                Map[x][y] = t;
            } else {
                a[x] = t;
            }
        }

        // 更新从i到j所需要时间加上玩游戏的时间
        for (int i = 1; i <= n; i++) {
            for (int j = 1; j <= n; j++) {
                if (i != j) {
                    Map[i][j] += a[j];
                }
            }
        }

        for (int i = 1; i <= n; i++) {
            f[i][i] = 0; // 由于玩游戏时间已经加在路程时间上了,因此从i到i时间应该为0
        }

        // 求解
        for (int k = 1; k <= n; k++) {
            for (int i = 1; i <= n; i++) {
                for (int j = 1; j <= n; j++) {
                    if (i == j) {
                        continue;
                    }
                    if (k == j) { // 中继点和终点相同,相当于直接从i前往j
                        f[i][j] = Math.min(f[i][j], f[i][k] + a[k]);
                    } else { // 中继点和终点不同,通过中继点更新
                        f[i][j] = Math.min(f[i][j], f[i][k] + f[k][j]);
                    }
                }
            }
        }

        s = scanner.nextInt();
        int sum = 0;
        int maxx = 0;
        for (int i = 1; i <= n; i++) {
            if (i == s) {
                continue;
            }
            if (f[s][i] < Integer.MAX_VALUE / 2) {
                sum++;
                maxx = Math.max(maxx, f[s][i]);
            }
        }

        System.out.println((sum + 1) + "\n" + maxx); // 别忘记答案加上起点
    }
}

Go

package main

import (
	"fmt"
)

func min(a, b int) int {
	if a < b {
		return a
	}
	return b
}

func max(a, b int) int {
	if a > b {
		return a
	}
	return b
}

func main() {
	var n, m int
	fmt.Scanf("%d %d", &n, &m)
	Map := make([][]int, 105)
	f := make([][]int, 105)
	for i := range Map {
		Map[i] = make([]int, 105)
		f[i] = make([]int, 105)
	}
	a := make([]int, 105)
	var s int

	// floyd算法
	// 初始化
	// 初始化为极大值
	for i := 1; i <= n; i++ {
		for j := 1; j <= n; j++ {
			f[i][j] = 1 << 30
		}
	}

	for i := 1; i <= m; i++ {
		var x, y, t int
		fmt.Scanf("%d %d %d", &x, &y, &t)
		if x != y {
			Map[x][y] = t
		} else {
			a[x] = t
		}
	}

	// 更新从i到j所需要时间加上玩游戏的时间
	for i := 1; i <= n; i++ {
		for j := 1; j <= n; j++ {
			if i != j {
				Map[i][j] += a[j]
			}
			f[i][j] = Map[i][j]
		}
	}

	// 求解
	for k := 1; k <= n; k++ {
		for i := 1; i <= n; i++ {
			for j := 1; j <= n; j++ {
				if i == j {
					continue
				}
				if k == j { // 中继点和终点相同,相当于直接从i前往j
					f[i][j] = min(f[i][j], f[i][k]+a[k])
				} else { // 中继点和终点不同,通过中继点更新
					f[i][j] = min(f[i][j], f[i][k]+f[k][j])
				}
			}
		}
	}

	fmt.Scanf("%d", &s)
	sum := 0
	maxx := 0
	for i := 1; i <= n; i++ {
		if i == s {
			continue
		}
		if f[s][i] < 1<<30 {
			sum++
			maxx = max(maxx, f[s][i])
		}
	}

	fmt.Printf("%d\n%d\n", sum+1, maxx) // 别忘记答案加上起点
}

Js

function min(a, b) {
  return a < b ? a : b;
}

function max(a, b) {
  return a > b ? a : b;
}

function main() {
  const readline = require('readline');
  const rl = readline.createInterface({
    input: process.stdin,
    output: process.stdout
  });

  rl.on('line', function(line) {
    const input = line.trim().split(' ').map(Number);
    const n = input[0];
    const m = input[1];
    const Map = Array.from({ length: 105 }, () => Array(105).fill(0));
    const f = Array.from({ length: 105 }, () => Array(105).fill(0));
    const a = Array(105).fill(0);
    let s;

    // floyd算法
    // 初始化
    // 初始化为极大值
    for (let i = 1; i <= n; i++) {
      for (let j = 1; j <= n; j++) {
        f[i][j] = Infinity;
      }
    }

    let count = 0;
    rl.on('line', function(line) {
      const input = line.trim().split(' ').map(Number);
      const x = input[0];
      const y = input[1];
      const t = input[2];
      if (x !== y) {
        Map[x][y] = t;
      } else {
        a[x] = t;
      }
      count++;
      if (count === m) {
        rl.close();
      }
    });

    rl.on('close', function() {
      // 更新从i到j所需要时间加上玩游戏的时间
      for (let i = 1; i <= n; i++) {
        for (let j = 1; j <= n; j++) {
          if (i !== j) {
            Map[i][j] += a[j];
          }
          f[i][j] = Map[i][j];
        }
      }

      // 求解
      for (let k = 1; k <= n; k++) {
        for (let i = 1; i <= n; i++) {
          for (let j = 1; j <= n; j++) {
            if (i === j) {
              continue;
            }
            if (k === j) { // 中继点和终点相同,相当于直接从i前往j
              f[i][j] = min(f[i][j], f[i][k] + a[k]);
            } else { // 中继点和终点不同,通过中继点更新
              f[i][j] = min(f[i][j], f[i][k] + f[k][j]);
            }
          }
        }
      }

      rl.on('line', function(line) {
        s = Number(line.trim());

        let sum = 0;
        let maxx = 0;
        for (let i = 1; i <= n; i++) {
          if (i === s) {
            continue;
          }
          if (f[s][i] < Infinity) {
            sum++;
            maxx = max(maxx, f[s][i]);
          }
        }

        console.log((sum + 1) + '\n' + maxx); // 别忘记答案加上起点
      });
    });

题目内容均收集自互联网,如如若此项内容侵犯了原著者的合法权益,可联系我: (CSDN网站注册用户名: 塔子哥学算法) 进行删除。

华为OD机试 数大雁是华为公司招聘流程中的其中一项测试环节,用于考察应聘者在编程方面的能力。该笔试主要考察的是计算机基础知识和编程实战能力华为OD机试目通常由一系列算法编程组成,其中涵盖了数据结构和算法、字符串操作、动态规划、图论、递归、搜索等各个方面的知识点。考生需要根据目要求,自行设计算法并编写相应的代码,来解决问。 这一环节的目的是为了考察应聘者在编程方面的能力,包括对算法的理解和实现能力、代码质量和效率等方面的评估。编程能力在今天的软件工程领域中十分重要,能够有效地解决实际问,提高代码的可读性和可维护性,是评估一个程序员水平的重要指标。 参加华为OD机试数字大雁,需要具备扎实的计算机基础知识,熟练掌握编程语言和常用的数据结构和算法,并具备理解和分析问能力。在备战笔试的过程中,应聘者应该注重对算法的学习和理解,并进行大量的编程实践,深入理解各类算法的原理与应用场景。在解答算法时,可以运用递归、分治、贪心、动态规划等常用思想,将问拆解为更小的子问,从而更好地解决复杂的算法华为OD机试数字大雁是一个对程序员编程能力的一种考察方式,参加者需要通过编写代码解决目,展示自己的编程实力。准备过程中,应聘者应该注意提高自己的算法能力,并进行足够的练习,积累编程经验,从而顺利通过华为OD机试数字大雁。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

塔子哥学算法

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

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

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

打赏作者

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

抵扣说明:

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

余额充值