20天拿下华为OD笔试之【回溯】2023Q1A-基站维修工程师【欧弟算法】全网注释最详细分类最全的华为OD真题题解

news/2024/7/20 16:49:50 标签: 华为od, 算法, leetcode, 算法, python

文章目录

  • 【回溯】2023Q1A-基站维修工程师
  • 题目描述与示例
    • 题目
    • 输入
    • 输出描述
    • 示例一
      • 输入
      • 输出
      • 说明
  • 解题思路
    • 剪枝优化
  • 时空复杂度
  • 代码
    • 解法一:回溯(无剪枝优化)
    • 解法二:回溯(剪枝优化)

【回溯】2023Q1A-基站维修工程师

题目描述与示例

题目

小王是一名基站维护工程师,负责某区域的基站维护。 某地方有 n 个基站,1 < n < 10,已知各基站之间的距离 s0 < s < 500,并且基站 x 到基站 y 的距离,与基站 y 到 基站 x 的距离并不一定会相同。 小王从基站 0 出发,途经每个基站 1 次,然后返回基站 0 ,需要请你为他选择一条距离最短的路。

输入

站点数 n 和各站点之间的距离,均为整数。

3     // 表示站点数
0 2 1 // 表示站点0到各站点的路程
1 0 2 // 表示站点1到各站点的路程
2 1 0 // 表示站点3到各站点的路程

输出描述

最短路程的数值。

示例一

输入

3
0 2 1
1 0 2
2 1 0

输出

3

说明

路线为0 -> 2 -> 1 -> 0,距离为所有路线最小 1 + 1 + 1 = 3。如果选择路线0 -> 1 -> 2 -> 0,距离为 2 + 2 + 2 = 6

解题思路

这道题本质上是一道排列类型的回溯问题。

路径的终点和起点是都定好的,均为基站0,且其他基站能且仅能通过1次。所以我们只需要考虑剩余n-1个基站的全排列方式即可,即一共有(n-1)!种排列方式。

假设一共有 4 个基站,我们有且只有以下6种可能的路径:

0 -> 1 -> 2 -> 3 -> 0
0 -> 1 -> 3 -> 2 -> 0
0 -> 2 -> 1 -> 3 -> 0
0 -> 2 -> 3 -> 1 -> 0
0 -> 3 -> 1 -> 2 -> 0
0 -> 3 -> 2 -> 1 -> 0

所以可以使用回溯的办法,暴力地枚举出所有路径的距离和,并且获得所有距离和中的最小值即可。

剪枝优化

本题还可以进行剪枝优化。思路非常直接:如果某条路径的距离和path_sum已经大于等于当前的ans了,那么这条路径再往下延申,也一定会比ans更大,那么这条路径再往下搜寻就没有意义了,可以进行剪枝。

时空复杂度

时间复杂度:O(N!)。一共有(n-1)!条路径需要计算路径和。

空间复杂度:O(N)。忽略调用递归函数时编译栈所占空间,仅考虑检查数组所占用空间。

代码

解法一:回溯(无剪枝优化)

python"># 题目:2023Q1A-基站维修工程师
# 作者:闭着眼睛学数理化
# 算法>算法:回溯-无剪枝优化
# 代码看不懂的地方,请直接在群上提问

from math import inf

n = int(input())
mat = list()
for i in range(n):
    mat.append(list(map(int, input().split())))

# 用于判断某一节点是否被包含在当前路径中的检查数组
check_list = [False] * n
# 初始化位置0已经被包含在路径中
check_list[0] = True
# 初始化
ans = inf

# 回溯函数
def dfs(mat, check_list, cur_idx, path_sum):
    # 声明ans为全局变量,需要反复地被更新
    global ans
    # 终止递归条件:
    # 当check_list中所有值均为True,说明得到了一条经过了所有点的路径
    # 当前路径和path_sum还需要再加上从位置cur_idx去往位置0的距离
    if all(check_list):
        ans = min(ans, path_sum + mat[cur_idx][0])
        return
    # 横向遍历cur_idx到下一个点nxt_idx的所有距离
    for nxt_idx, val in enumerate(mat[cur_idx]):
        # 只有当nxt_idx还未存在于路径中,cur_idx才可以到达
        if check_list[nxt_idx] == False:
            # 状态更新:
            # 1. 标记下一个点nxt_idx已经存在于路径中
            # 2. 将从cur_idx到nxt_idx的距离val加入当前路径总和中
            check_list[nxt_idx] = True
            path_sum += val
            # 回溯:对下一个点nxt_idx进行递归
            dfs(mat, check_list, nxt_idx, path_sum)
            # 回滚:
            # 1. 标记下一个点nxt_idx从路径中删除
            # 2. 将从cur_idx到nxt_idx的距离val从当前路径总和中减去
            check_list[nxt_idx] = False
            path_sum -= val

# 调用递归函数的入口,最开始cur_idx = 0,path_sum = 0
dfs(mat, check_list, 0, 0)
print(ans)

解法二:回溯(剪枝优化)

python"># 题目:2023Q1A-基站维修工程师
# 作者:闭着眼睛学数理化
# 算法>算法:回溯-剪枝优化
# 代码看不懂的地方,请直接在群上提问

from math import inf

n = int(input())
mat = list()
for i in range(n):
    mat.append(list(map(int, input().split())))

# 用于判断某一节点是否被包含在当前路径中的检查数组
check_list = [False] * n
# 初始化位置0已经被包含在路径中
check_list[0] = True
# 初始化
ans = inf

# 回溯函数
def dfs(mat, check_list, cur_idx, path_sum):
    # 声明ans为全局变量,需要反复地被更新
    global ans
    # 剪枝优化:
    # 如果当前路径和path_sum已经大于等于当前ans
    # 这条路径再往下搜寻没有意义,不可能比ans更小,进行剪枝,终止当前路径继续往下搜寻
    if path_sum >= ans:
        return
    # 终止递归条件:
    # 当check_list中所有值均为True,说明得到了一条经过了所有点的路径
    # 当前路径和path_sum还需要再加上从位置cur_idx去往位置0的距离
    if all(check_list):
        ans = min(ans, path_sum + mat[cur_idx][0])
        return
    # 横向遍历cur_idx到下一个点nxt_idx的所有距离
    for nxt_idx, val in enumerate(mat[cur_idx]):
        # 只有当nxt_idx还未存在于路径中,cur_idx才可以到达
        if check_list[nxt_idx] == False:
            # 状态更新:
            # 1. 标记下一个点nxt_idx已经存在于路径中
            # 2. 将从cur_idx到nxt_idx的距离val加入当前路径总和中
            check_list[nxt_idx] = True
            path_sum += val
            # 回溯:对下一个点nxt_idx进行递归
            dfs(mat, check_list, nxt_idx, path_sum)
            # 回滚:
            # 1. 标记下一个点nxt_idx从路径中删除
            # 2. 将从cur_idx到nxt_idx的距离val从当前路径总和中减去
            check_list[nxt_idx] = False
            path_sum -= val

# 调用递归函数的入口,最开始cur_idx = 0,path_sum = 0
dfs(mat, check_list, 0, 0)
print(ans)

http://www.niftyadmin.cn/n/5194273.html

相关文章

hyperledger fabric2.4测试网络添加组织数量

!!!修改内容比较繁琐,预期未来提供模板修改 修改初始配置文件,初始添加3个组织 organizations文件夹 /cryptogen文件夹下创建文件crypto-config-org3.yaml,内容如下: PeerOrgs:# ---------------------------------------------------------------------------# Org3# ----…

C语言-统计字符

本题要求编写程序&#xff0c;输入10个字符&#xff0c;统计其中英文字母、空格或回车、数字字符和其他字符的个数。 输入格式: 输入为10个字符。最后一个回车表示输入结束&#xff0c;不算在内。 输出格式: 在一行内按照 letter 英文字母个数, blank 空格或回车个数, d…

量化交易:建立趋势跟踪策略的五个指标

什么是趋势跟踪策略&#xff1f; 趋势跟踪策略是只需需顺势而为的策略&#xff0c;即在价格上涨时买入&#xff0c;在价格开始下跌时卖出。在趋势跟踪策略中&#xff0c;人们的目标不是预测或预测&#xff0c;而只是关注市场上的任何新兴趋势。 趋势是如何出现的&#xff1f;…

Shell判断:流程控制—if(二)

一、多分支结构 1、语法&#xff1a; if 条件测试1 then 命令序列 elif 条件测试2 then 命令序列 elif 条件测试3 then 命令序列.... else 命令序列 fi 2、示例&am…

NSSCTF第13页(2)

[HNCTF 2022 Week1]Challenge__rce 提示?hint 访问看到了源码 <?php error_reporting(0); if (isset($_GET[hint])) { highlight_file(__FILE__); } if (isset($_POST[rce])) { $rce $_POST[rce]; if (strlen($rce) < 120) { if (is_string($rce…

十七、Linux的组管理

1、Linux组基本介绍 在linux中的每个用户必须属于一个组&#xff0c;不能独立于组外。在linux中每个文件所有者、所在组、其它组的概念 1.所有者 2.所在组 3.其他组 4.改变用户所在的组 2、文件/目录 所有者 一般为文件的创建者&#xff0c;谁创建了该文件&#xff0c;就自…

linux中编写.sh脚本并赋权限问题

以项目启动、重启、终止脚本为例&#xff1a; 步骤&#xff1a; 首先vi start.sh、 vi restart.sh、 vi stop.sh或者使用vim编辑器&#xff1b; 编辑内容&#xff1a; 启动&#xff1a;vi start.sh #!/bin/bash nohup java -jar jeewx-boot-start-1.0.0.jar >catalina.…

局域网内Ubuntu上搭建Git服务器

1.在局域网内选定一台Ubuntu电脑作为Git服务端&#xff1a; (1).新建用户如为fbc&#xff0c;执行如下命令&#xff1a;需设置密码&#xff0c;此为fbc sudo adduser fbc (2).切换到fbc用户&#xff1a;需密码&#xff0c;此前设置为fbc su fbc (3).建一个空目录作为仓…