6.22华为OD机试真题 新系统 - 数据包分段传输的最小最大延迟 (Java/Py/C/C++/Js/Go)
·
数据包分段传输的最小最大延迟
2026 华为OD机试真题 6月22日华为OD上机新系统考试真题 200 分题型
点击查看华为 OD 机试真题完整目录:2026最新华为OD机试新系统卷 + 双机位C卷 真题题库目录|全覆盖题库 + 逐点算法考点详解
题目描述
在通信系统中,有一组数据包需要按顺序通过 k 个通道传输。每个数据包 i 的传输耗时为 nums[i](单位:毫秒)。
传输规则
- 同一通道内的数据包必须按顺序连续传输,即若数据包 [i, i+1, …, j] 分配给同一通道,则该通道的传输总延迟为 nums[i]+nums[i+1]+…+nums[j]。
- 不同通道之间可以并行传输,系统整体延迟取决于延迟最大的那个通道。
- 数据包只能被拆分为连续子段,例如 [1, 2, 3] 可以被拆分为 [1, 2], [3],但是不能被拆分为 [1, 3], [2]。
你的任务是:将 nums 数组划分到 k 个连续非空通道,使得传输总耗时最小。
输入描述
- nums:非负整数数组,长度 n(1≤n≤1000),每个元素满足 0≤nums[j]≤106。
- k:整数,通道数量(1≤k≤n)。
输出描述
- 返回一个整数,值为符合题意的最优传输策略下的总耗时。
示例1
输入
7,2,5,10,8
2
输出
18
说明
输入: nums=[7, 2, 5, 10, 8], k=2 输出: 18 解释: 共有以下划分方案(划分为2个连续子数组): 方案1: [7], [2,5,10,8]→ 总耗时为 25 方案2: [7,2], [5,10,8]→ 总耗时为 23 方案3: [7,2,5], [10,8]→ 总耗时为 18← 最优 方案4: [7,2,5,10], [8]→ 总耗时为 24 因此最优总耗时为 18。
示例2
输入
[1, 2, 3, 4, 5],2
输出
9
说明
输入: nums=[1, 2, 3, 4, 5], k=2 输出: 9 解释: 最优划分为 [1,2,3] 和 [4,5],最优总耗时为 9。
示例3
输入
1, 4, 4
3
输出
4
说明
输入: nums=[1, 4, 4], k=3 输出: 4 解释: 划分为 [1], [4], [4],最优总耗时为 4。
解题思路
本题要求将数组划分为 k 个连续非空子段,使得所有子段和的最大值最小。这是一个经典的「分割数组/划分问题」,常使用二分查找 + 贪心验证策略求解。
关键洞察:
- 如果某个阈值
limit可行(能将数组划分为不超过 k 个子段,且每个子段和 ≤ limit),那么所有更大的阈值也一定可行。 - 如果某个阈值
limit不可行,那么所有更小的阈值也一定不可行。 - 这种单调性使得我们可以使用二分查找找到最小的可行阈值。
算法流程:
- 确定搜索范围:下界为数组中的最大值(每个数据包必须单独传输),上界为数组所有元素之和(所有数据包在一个通道)。
- 二分查找:对于每个候选阈值
mid,使用贪心算法检查能否将数组划分为不超过 k 个子段。 - 贪心验证:遍历数组,尽量在当前段中添加元素,如果添加后超过
mid,则开启新段。最终统计所需段数是否 ≤ k。
复杂度分析
- 时间复杂度:O(n × log(sum)),其中 n 是数组长度,sum 是数组元素之和。二分查找 log(sum) 次,每次验证 O(n)。
- 空间复杂度:O(1),只使用了常数额外空间。
Java
import java.util.Scanner;
public class Main {
// 检查在给定的最大延迟限制下,是否能将数据包划分为不超过 k 个通道
private static boolean canSplit(int[] packets, long limit, int k) {
int segments = 1; // 当前使用的通道数
long currentSum = 0; // 当前通道的累计延迟
for (int packet : packets) {
// 如果当前通道加上这个数据包会超过限制,则开启新通道
if (currentSum + packet > limit) {
segments++; // 新增一个通道
currentSum = 0; // 重置当前通道累计延迟
}
currentSum += packet; // 将数据包加入当前通道
}
// 所需通道数不超过 k 则该 limit 可行
return segments <= k;
}
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
// 读取第一行:数据包大小数组
String line = scanner.nextLine().trim();
// 解析数组,处理 [ ] 和空格
line = line.replaceAll("[\\[\\] ]", "");
String[] parts = line.split(",");
int n = parts.length;
int[] packets = new int[n];
for (int i = 0; i < n; i++) {
packets[i] = Integer.parseInt(parts[i]);
}
// 读取第二行:通道数量
int k = Integer.parseInt(scanner.nextLine().trim());
// 二分查找求解最小最大延迟
int left = 0, right = 0;
for (int packet : packets) {
left = Math.max(left, packet); // 下界:单个最大数据包
right += packet; // 上界:所有数据包之和
}
while (left < right) {
int mid = left + (right - left) / 2;
if (canSplit(packets, mid, k)) {
right = mid; // mid 可行,尝试更小的限制
} else {
left = mid + 1; // mid 不可行,需要增大限制
}
}
System.out.println(left);
scanner.close();
}
}
Python
import sys
def can_split(packets, limit, k):
"""
贪心验证:检查在给定的最大延迟限制下,是否能将数据包划分为不超过 k 个通道
参数:
packets: 数据包大小列表
limit: 允许的单通道最大延迟
k: 可用的通道数量
返回:
bool: 如果能够划分为不超过 k 个通道则返回 True
"""
segments = 1 # 当前划分的段数(至少1段)
current_sum = 0 # 当前段的累计延迟
for packet in packets:
# 如果当前段加上此数据包会超过限制,则开启新段
if current_sum + packet > limit:
segments += 1
current_sum = 0
current_sum += packet
return segments <= k
def minimum_latency(packets, k):
"""
二分查找求解:将数组划分为 k 个连续子段,使得最大子段和最小
参数:
packets: 数据包大小列表
k: 要划分的段数
返回:
int: 最小的最大延迟
"""
# 确定搜索范围
left = max(packets) # 下界:必须至少能容纳最大的数据包
right = sum(packets) # 上界:所有数据包放在一个通道
while left < right:
mid = (left + right) // 2
if can_split(packets, mid, k):
right = mid # mid 可行,尝试更小的值
else:
left = mid + 1 # mid 不可行,需要增大
return left
def main():
lines = [line.strip() for line in sys.stdin if line.strip()]
if not lines:
return
# 解析第一行:数据包数组
nums_str = lines[0].replace('[', '').replace(']', '').replace(' ', '')
packets = list(map(int, nums_str.split(',')))
# 解析第二行:通道数量
k = int(lines[1])
result = minimum_latency(packets, k)
print(result)
if __name__ == "__main__":
main()
JavaScript
const readline = require('readline');
function can_split(packets, limit, k) {
/*
* 贪心验证:检查在给定的最大延迟限制下,是否能将数据包划分为不超过 k 个通道
*/
let segments = 1;
let current_sum = 0;
for (const packet of packets) {
if (current_sum + packet > limit) {
segments += 1;
current_sum = 0;
}
current_sum += packet;
}
return segments <= k;
}
function minimum_latency(packets, k) {
/*
* 二分查找求解:将数组划分为 k 个连续子段,使得最大子段和最小
*/
let left = Math.max(...packets);
let right = packets.reduce((sum, x) => sum + x, 0);
while (left < right) {
const mid = Math.floor((left + right) / 2);
if (can_split(packets, mid, k)) {
right = mid;
} else {
left = mid + 1;
}
}
return left;
}
function main() {
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout
});
const lines = [];
rl.on('line', (line) => {
const trimmed = line.trim();
if (trimmed) {
lines.push(trimmed);
}
});
rl.on('close', () => {
if (lines.length === 0) {
return;
}
const nums_str = lines[0].replace(/\[/g, '').replace(/\]/g, '').replace(/\s/g, '');
const packets = nums_str.split(',').map(Number);
const k = Number(lines[1]);
const result = minimum_latency(packets, k);
console.log(result);
});
}
main();
C++
#include <iostream>
#include <vector>
#include <algorithm>
#include <sstream>
#include <string>
using namespace std;
// 贪心验证:检查在给定的最大延迟限制下,是否能将数据包划分为不超过 k 个通道
bool canSplit(const vector<int>& packets, long long limit, int k) {
int segments = 1; // 当前划分的段数(至少1段)
long long currentSum = 0; // 当前段的累计延迟
for (int packet : packets) {
// 如果当前段加上此数据包会超过限制,则开启新段
if (currentSum + packet > limit) {
segments++;
currentSum = 0;
}
currentSum += packet;
}
return segments <= k;
}
// 二分查找求解最小最大延迟
int minimumLatency(vector<int>& packets, int k) {
// 确定搜索范围
int left = *max_element(packets.begin(), packets.end()); // 下界:最大数据包
int right = 0;
for (int p : packets) right += p; // 上界:所有数据包之和
while (left < right) {
int mid = left + (right - left) / 2;
if (canSplit(packets, mid, k)) {
right = mid; // mid 可行,尝试更小的值
} else {
left = mid + 1; // mid 不可行,需要增大
}
}
return left;
}
// 解析输入字符串,提取数字数组
vector<int> parseArray(const string& str) {
vector<int> result;
string s = str;
// 移除括号和空格
s.erase(remove(s.begin(), s.end(), '['), s.end());
s.erase(remove(s.begin(), s.end(), ']'), s.end());
s.erase(remove(s.begin(), s.end(), ' '), s.end());
stringstream ss(s);
string token;
while (getline(ss, token, ',')) {
if (!token.empty()) {
result.push_back(stoi(token));
}
}
return result;
}
int main() {
string line1, line2;
// 读取第一行:数据包数组
getline(cin, line1);
// 读取第二行:通道数量
getline(cin, line2);
// 解析输入
vector<int> packets = parseArray(line1);
int k = stoi(line2);
// 计算并输出结果
int result = minimumLatency(packets, k);
cout << result << endl;
return 0;
}
Go
package main
import (
"bufio"
"fmt"
"os"
"strconv"
"strings"
)
// canSplit 贪心验证:检查在给定的最大延迟限制下,是否能将数据包划分为不超过 k 个通道
func canSplit(packets []int, limit int, k int) bool {
segments := 1 // 当前划分的段数(至少1段)
currentSum := 0 // 当前段的累计延迟
for _, packet := range packets {
// 如果当前段加上此数据包会超过限制,则开启新段
if currentSum+packet > limit {
segments++
currentSum = 0
}
currentSum += packet
}
return segments <= k
}
// minimumLatency 二分查找求解:将数组划分为 k 个连续子段,使得最大子段和最小
func minimumLatency(packets []int, k int) int {
// 确定搜索范围
left := packets[0]
right := 0
for _, p := range packets {
if p > left {
left = p
}
right += p
}
for left < right {
mid := (left + right) / 2
if canSplit(packets, mid, k) {
right = mid // mid 可行,尝试更小的值
} else {
left = mid + 1 // mid 不可行,需要增大
}
}
return left
}
// parseArray 解析输入字符串,提取数字数组
func parseArray(s string) []int {
// 移除括号和空格
s = strings.ReplaceAll(s, "[", "")
s = strings.ReplaceAll(s, "]", "")
s = strings.ReplaceAll(s, " ", "")
parts := strings.Split(s, ",")
result := make([]int, 0, len(parts))
for _, part := range parts {
if part == "" {
continue
}
num, err := strconv.Atoi(part)
if err == nil {
result = append(result, num)
}
}
return result
}
func main() {
scanner := bufio.NewScanner(os.Stdin)
// 读取第一行:数据包数组
var line1, line2 string
if scanner.Scan() {
line1 = scanner.Text()
}
// 读取第二行:通道数量
if scanner.Scan() {
line2 = scanner.Text()
}
// 解析输入
packets := parseArray(strings.TrimSpace(line1))
k, _ := strconv.Atoi(strings.TrimSpace(line2))
// 计算并输出结果
result := minimumLatency(packets, k)
fmt.Println(result)
}
C语言
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <limits.h>
#define MAX_LEN 10000
// 贪心验证:检查在给定的最大延迟限制下,是否能将数据包划分为不超过 k 个通道
int canSplit(int* packets, int n, int limit, int k) {
int segments = 1; // 当前划分的段数(至少1段)
int currentSum = 0; // 当前段的累计延迟
for (int i = 0; i < n; i++) {
// 如果当前段加上此数据包会超过限制,则开启新段
if (currentSum + packets[i] > limit) {
segments++;
currentSum = 0;
}
currentSum += packets[i];
}
return segments <= k;
}
// 二分查找求解最小最大延迟
int minimumLatency(int* packets, int n, int k) {
// 确定搜索范围
int left = packets[0];
int right = 0;
for (int i = 0; i < n; i++) {
if (packets[i] > left) {
left = packets[i];
}
right += packets[i];
}
while (left < right) {
int mid = left + (right - left) / 2;
if (canSplit(packets, n, mid, k)) {
right = mid; // mid 可行,尝试更小的值
} else {
left = mid + 1; // mid 不可行,需要增大
}
}
return left;
}
// 解析输入字符串,提取数字数组
int parseArray(const char* str, int* packets) {
char s[MAX_LEN];
strcpy(s, str);
// 移除括号和空格
char* src = s;
char* dst = s;
while (*src) {
if (*src != '[' && *src != ']' && *src != ' ') {
*dst++ = *src;
}
src++;
}
*dst = '\0';
int count = 0;
char* token = strtok(s, ",");
while (token != NULL) {
packets[count++] = atoi(token);
token = strtok(NULL, ",");
}
return count;
}
int main() {
char line1[MAX_LEN];
char line2[MAX_LEN];
// 读取第一行:数据包数组
fgets(line1, MAX_LEN, stdin);
line1[strcspn(line1, "\n")] = 0;
// 读取第二行:通道数量
fgets(line2, MAX_LEN, stdin);
line2[strcspn(line2, "\n")] = 0;
// 解析输入
int packets[MAX_LEN];
int n = parseArray(line1, packets);
int k = atoi(line2);
// 计算并输出结果
int result = minimumLatency(packets, n, k);
printf("%d\n", result);
return 0;
}
完整用例
用例1
7,2,5,10,8
2
用例2
1, 2, 3, 4, 5
2
用例3
1, 4, 4
3
用例4
10,20,30,40
1
用例5
5,5,5,5
4
用例6
3,1,2,7,4,6
3
用例7
100,200,150,300
2
用例8
1,1,1,1,1,1
2
用例9
5,10,15,20,25,30
3
用例10
0,5,0,10,0
3
文章目录

更多推荐
所有评论(0)