题解:AtCoder AT_awc0005_d Splitting Delivery Packages
【题目来源】
AtCoder:D - Splitting Delivery Packages
【题目描述】
Takahashi is in charge of delivery planning at a shipping company.
高桥负责一家货运公司的送货路线规划工作。
Today, he needs to create a plan to deliver \(N\) packages using \(K\) trucks.
今天,他需要制定一个使用 \(K\) 辆卡车运送 \(N\) 个包裹的计划。
Each package is numbered from \(1\) to \(N\), and the weight of package \(i\) is \(A_i\).
每个包裹编号从 \(1\) 到 \(N\),且包裹 \(i\) 的重量为 \(A_i\)。
To improve delivery efficiency, he decided to arrange the packages \(1, 2, \ldots, N\) in this order and divide them into \(K\) contiguous segments, each containing at least \(1\) package, and assign the packages in each segment to one truck. Every package belongs to exactly one segment, and there is a one-to-one correspondence between the \(K\) segments and the \(K\) trucks.
为了提高送货效率,他决定将包裹 \(1, 2, …, N\) 按此顺序排列,并将其分割为 \(K\) 个连续段(每段至少包含 \(1\) 个包裹),然后将每段中的包裹分配给一辆卡车。每个包裹恰好属于一个段,并且 \(K\) 个段与 \(K\) 辆卡车之间存在一一对应关系。
For each truck, the sum of the weights of the packages assigned to that truck is called the load of that truck.
对于每辆卡车,分配给该卡车的包裹重量之和称为该卡车的载重。
Takahashi wants to maximize the smallest load among the \(K\) trucks.
高桥希望最大化 \(K\) 辆卡车中最小的载重。
Considering all possible ways to divide the packages, find the maximum possible value of "the minimum load among the \(K\) trucks."
考虑所有可能的分割包裹方式,求"\(K\) 辆卡车中最小的载重"的最大可能值。
【输入】
\(N\) \(K\)
\(A_1\) \(A_2\) \(\ldots\) \(A_N\)
- The first line contains an integer \(N\) representing the number of packages and an integer \(K\) representing the number of trucks, separated by a space.
- The second line contains \(N\) integers \(A_1, A_2, \ldots, A_N\) representing the weight of each package, separated by spaces.
【输出】
Print in one line the maximum possible value of the minimum load among the \(K\) trucks when the packages are divided optimally.
【输入样例】
5 2
1 2 3 4 5
【输出样例】
6
【核心思想】
-
问题分析:给定 \(N\) 个包裹的重量 \(A_1, A_2, \ldots, A_N\),需要将其分割为 \(K\) 个连续段,每段分配给一辆卡车。目标是最大化 \(K\) 辆卡车中最小的载重。这是一个二分答案问题,具有单调性:如果最小载重 \(x\) 可行,则所有 \(\leq x\) 的值也可行(但我们要找最大的可行值)。
-
算法选择:
- 二分答案:在答案范围 \([0, \sum A_i]\) 内二分查找最大的可行最小载重
- 贪心判定:对于给定的最小载重 \(x\),使用贪心策略判断是否可以将数组分割为至少 \(K\) 段,使得每段和都 \(\geq x\)
-
关键步骤:
- 二分范围:\(l = 0, r = 10^{18}\)(或所有包裹重量之和)
- 判定函数
check(x):- 从左到右遍历数组,维护当前段的和 \(sum\)
- 若 \(sum + A[i] \geq x\):说明当前段可以结束,段数 \(ans\) 加 \(1\),重置 \(sum = 0\)
- 否则:将 \(A[i]\) 加入当前段,\(sum = sum + A[i]\)
- 返回 \(ans \geq K\)(是否能分割出至少 \(K\) 段,每段和都 \(\geq x\))
- 二分过程:
- 取 \(mid = (l + r + 1) / 2\)(上取整防死循环)
- 若
check(mid)为真:说明 \(mid\) 可行,尝试更大的值,令 \(l = mid\) - 否则:\(mid\) 不可行,尝试更小的值,令 \(r = mid - 1\)
- 循环结束后,\(l\) 即为最大的可行最小载重
-
时间/空间复杂度:
- 时间复杂度:\(O(N \log S)\),其中 \(S = \sum A_i\),二分 \(O(\log S)\),每次判定 \(O(N)\)
- 空间复杂度:\(O(N)\),存储包裹重量数组
-
二分答案与贪心判定的核心思想:
- 单调性分析:若最小载重 \(x\) 可行,则所有 \(< x\) 的值也可行(分段更容易),具有单调性,适合二分
- 贪心策略:对于固定的 \(x\),尽可能早地结束当前段(一旦达到 \(x\) 就分段),这样能给后面的段留出更多元素,是最优策略
- 判定与二分分离:将"判定是否可行"与"二分查找最优值"分离,降低问题复杂度
- 最大化最小值:二分答案的经典应用,通过判定函数将优化问题转化为判定问题
- 适用于最大化最小值、最小化最大值、可行性判定等场景
【解题思路】

【算法标签】
整数二分
【代码详解】
#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N = 100005;
int n, k; // n: 数组长度,k: 所需分段数
int a[N]; // 数组
// 检查是否能够将数组分割为至少k段,使得每段和至少为x
bool check(int x)
{
int sum = 0, ans = 0; // sum: 当前段的和,ans: 已分割的段数
for (int i = 1; i <= n; i++)
{
if (sum + a[i] >= x) // 如果将当前元素加入会使段和达到x
{
ans++; // 段数加1
sum = 0; // 重置当前段和
}
else
{
sum += a[i]; // 将当前元素加入当前段
}
}
return ans >= k; // 返回是否能分割出至少k段
}
signed main()
{
cin >> n >> k; // 读入数组长度和所需段数
for (int i = 1; i <= n; i++)
{
cin >> a[i]; // 读入数组
}
int l = 0, r = 1e18; // 二分查找范围
while (l < r) // 二分查找
{
int mid = (l + r + 1) / 2; // 计算中点
if (check(mid)) // 如果mid可行
{
l = mid; // 尝试更大的值
}
else
{
r = mid - 1; // 尝试更小的值
}
}
cout << l << endl; // 输出最大的满足条件的x
return 0;
}
【运行结果】
5 2
1 2 3 4 5
6
浙公网安备 33010602011771号