华为OD机试 - 基站维护工程师数 - 动态规划(Python/JS/C/C++ 2024 E卷 200分)

在这里插入图片描述

华为OD机试 2024E卷题库疯狂收录中,刷题点这里

专栏导读

本专栏收录于《华为OD机试真题(Python/JS/C/C++)》

刷的越多,抽中的概率越大,私信哪吒,备注华为OD,加入华为OD刷题交流群,每一题都有详细的答题思路、详细的代码注释、3个测试用例、为什么这道题采用XX算法、XX算法的适用场景,发现新题目,随时更新,全天CSDN在线答疑。

一、题目描述

小王是一名基站维护工程师,负责某区域的基站维护。某地方有 n 个基站 (1 < n < 10),已知各基站之间的距离 s (0 <= s < 500),并且基站 x 到基站 y 的距离,与基站 y 到基站 x 的距离并不一定会相同。

小王从基站 1 出发,途经每个基站 1 次,然后返回基站 1。需要请你为他选择一条距离最短的路径。

二、输入描述

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

三、输出描述

最短路径的数值。

四、测试用例

测试用例1:

1、输入

3
0 2 1
1 0 2
2 1 0

2、输出

3

3、说明

测试用例2:

1、输入

4
0 10 15 20
10 0 35 25
15 35 0 30
20 25 30 0

2、输出

80

3、说明

路径 1 → 2 → 4 → 3 → 1,总距离为10 + 25 + 30 + 15 = 80。

五、解题思路

该问题是典型的旅行商问题(Traveling Salesman Problem, TSP),其中小王需要从基站1出发,经过每个基站一次后返回基站1。由于基站数量 n 较小(1 < n < 10),可以使用动态规划(Dynamic Programming, DP)结合位掩码(Bitmasking)来高效地解决。

  1. 距离矩阵(二维数组):
    • 使用二维数组 s[n][n] 存储各基站之间的距离。
    • 由于距离不对称,即 s[i][j] 不一定等于 s[j][i],所以需要单独存储。
  2. 动态规划数组(二维数组):
    • 使用 dp[mask][i] 表示访问过的基站集合为 mask,且最后访问的基站是 i 的最短路径长度。
    • mask 使用位掩码表示,其中第 k 位为1表示第 k 个基站已被访问。
  3. 算法流程:
    • 初始化 dp 数组,将起点基站1(索引0)的状态设为0。
    • 遍历所有可能的基站访问状态,通过动态规划逐步更新每个状态下的最短路径长度。
    • 遍历所有可能的结束基站,计算返回起点的最短路径,并取最小值作为结果。

六、Python算法源码

import sys

def main():
    # 读取所有输入
    input = sys.stdin.read().split()
    index = 0
    # 读取基站数量
    n = int(input[index])
    index += 1
    # 初始化距离矩阵
    s = []
    for i in range(n):
        row = []
        for j in range(n):
            row.append(int(input[index]))
            index += 1
        s.append(row)
    
    # 全部基站的状态,使用位掩码表示
    FULL_MASK = (1 << n) - 1
    # 初始化动态规划数组,dp[mask][i] 表示访问过mask状态,最后访问的基站是i的最小距离
    dp = [[float('inf')] * n for _ in range(1 << n)]
    # 起点是基站1,对应索引0,访问了基站1的状态
    dp[1 << 0][0] = 0
    
    # 遍历所有可能的状态
    for mask in range(1 << n):
        for last in range(n):
            # 如果当前状态没有访问到last基站,跳过
            if not (mask & (1 << last)):
                continue
            # 尝试扩展到下一个未访问的基站
            for next in range(n):
                # 如果已经访问过next基站,跳过
                if mask & (1 << next):
                    continue
                # 更新新的状态
                newMask = mask | (1 << next)
                dp[newMask][next] = min(dp[newMask][next], dp[mask][last] + s[last][next])
    
    # 最终结果,回到起点基站1
    res = float('inf')
    for last in range(n):
        if last == 0:
            continue  # 不考虑起点自身
        res = min(res, dp[FULL_MASK][last] + s[last][0])
    # 输出最短路径
    print(res)

if __name__ == "__main__":
    main()

七、JavaScript算法源码

const readline = require('readline');

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

let input = [];
rl.on('line', (line) => {
    input = input.concat(line.trim().split(/\s+/).map(Number));
}).on('close', () => {
    let index = 0;
    // 读取基站数量
    const n = input[index++];
    // 初始化距离矩阵
    const s = [];
    for(let i = 0; i < n; i++) {
        const row = [];
        for(let j = 0; j < n; j++) {
            row.push(input[index++]);
        }
        s.push(row);
    }
    
    // 全部基站的状态,使用位掩码表示
    const FULL_MASK = (1 << n) - 1;
    // 初始化动态规划数组,dp[mask][i] 表示访问过mask状态,最后访问的基站是i的最小距离
    const dp = Array.from({length: 1 << n}, () => Array(n).fill(Infinity));
    // 起点是基站1,对应索引0,访问了基站1的状态
    dp[1 << 0][0] = 0;
    
    // 遍历所有可能的状态
    for(let mask = 0; mask < (1 << n); mask++) {
        for(let last = 0; last < n; last++) {
            // 如果当前状态没有访问到last基站,跳过
            if( (mask & (1 << last)) === 0 ) continue;
            // 尝试扩展到下一个未访问的基站
            for(let next = 0; next < n; next++) {
                // 如果已经访问过next基站,跳过
                if(mask & (1 << next)) continue;
                // 更新新的状态
                const newMask = mask | (1 << next);
                dp[newMask][next] = Math.min(dp[newMask][next], dp[mask][last] + s[last][next]);
            }
        }
    }
    
    // 最终结果,回到起点基站1
    let res = Infinity;
    for(let last = 0; last < n; last++) {
        if(last === 0) continue; // 不考虑起点自身
        res = Math.min(res, dp[FULL_MASK][last] + s[last][0]);
    }
    // 输出最短路径
    console.log(res);
});

八、C算法源码

#include <stdio.h>
#include <limits.h>

int main(){
    int n;
    // 读取基站数量
    scanf("%d", &n);
    // 初始化距离矩阵
    int s[n][n];
    for(int i=0;i<n;i++){
        for(int j=0;j<n;j++){
            scanf("%d", &s[i][j]);
        }
    }
    
    // 全部基站的状态,使用位掩码表示
    int FULL_MASK = (1 << n) - 1;
    // 初始化动态规划数组,dp[mask][i] 表示访问过mask状态,最后访问的基站是i的最小距离
    long long dp[1<<n][n];
    for(int mask=0; mask<(1<<n); mask++){
        for(int i=0; i<n; i++){
            dp[mask][i] = LLONG_MAX / 2; // 避免溢出
        }
    }
    // 起点是基站1,对应索引0,访问了基站1的状态
    dp[1<<0][0] = 0;
    
    // 遍历所有可能的状态
    for(int mask=0; mask<(1<<n); mask++){
        for(int last=0; last<n; last++){
            // 如果当前状态没有访问到last基站,跳过
            if( (mask & (1<<last)) == 0 ) continue;
            // 尝试扩展到下一个未访问的基站
            for(int next=0; next<n; next++){
                // 如果已经访问过next基站,跳过
                if( mask & (1<<next) ) continue;
                // 更新新的状态
                int newMask = mask | (1<<next);
                if(dp[newMask][next] > dp[mask][last] + s[last][next]){
                    dp[newMask][next] = dp[mask][last] + s[last][next];
                }
            }
        }
    }
    
    // 最终结果,回到起点基站1
    long long res = LLONG_MAX;
    for(int last=0; last<n; last++){
        if(last == 0) continue; // 不考虑起点自身
        if(res > dp[FULL_MASK][last] + s[last][0]){
            res = dp[FULL_MASK][last] + s[last][0];
        }
    }
    // 输出最短路径
    printf("%lld\n", res);
    return 0;
}

九、C++算法源码

#include <bits/stdc++.h>
using namespace std;

int main(){
    ios::sync_with_stdio(false);
    cin.tie(0);
    int n;
    // 读取基站数量
    cin >> n;
    // 初始化距离矩阵
    vector<vector<int>> s(n, vector<int>(n));
    for(int i=0;i<n;i++) {
        for(int j=0;j<n;j++) {
            cin >> s[i][j];
        }
    }
    
    // 全部基站的状态,使用位掩码表示
    int FULL_MASK = (1 << n) - 1;
    // 初始化动态规划数组,dp[mask][i] 表示访问过mask状态,最后访问的基站是i的最小距离
    vector<vector<long long>> dp(1<<n, vector<long long>(n, LLONG_MAX / 2));
    // 起点是基站1,对应索引0,访问了基站1的状态
    dp[1<<0][0] = 0;
    
    // 遍历所有可能的状态
    for(int mask=0; mask<(1<<n); mask++){
        for(int last=0; last<n; last++){
            // 如果当前状态没有访问到last基站,跳过
            if( (mask & (1<<last)) == 0 ) continue;
            // 尝试扩展到下一个未访问的基站
            for(int next=0; next<n; next++){
                // 如果已经访问过next基站,跳过
                if(mask & (1<<next)) continue;
                // 更新新的状态
                int newMask = mask | (1<<next);
                dp[newMask][next] = min(dp[newMask][next], dp[mask][last] + (long long)s[last][next]);
            }
        }
    }
    
    // 最终结果,回到起点基站1
    long long res = LLONG_MAX;
    for(int last=0; last<n; last++){
        if(last == 0) continue; // 不考虑起点自身
        res = min(res, dp[FULL_MASK][last] + (long long)s[last][0]);
    }
    // 输出最短路径
    cout << res;
    return 0;
}


🏆下一篇:华为OD机试真题 - 简易内存池(Python/JS/C/C++ 2024 E卷 200分)

🏆本文收录于,华为OD机试真题(Python/JS/C/C++)

刷的越多,抽中的概率越大,私信哪吒,备注华为OD,加入华为OD刷题交流群,每一题都有详细的答题思路、详细的代码注释、3个测试用例、为什么这道题采用XX算法、XX算法的适用场景,发现新题目,随时更新,全天CSDN在线答疑。

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

哪 吒

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

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

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

打赏作者

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

抵扣说明:

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

余额充值