二分答案
二分答案是一种高效的算法技巧,通常用于解决最优化问题,尤其是当问题具有单调性时。它的核心思想是通过二分查找来快速缩小答案的范围,从而找到最优解。
适用场景
二分答案常用于以下类型的问题:
- 最大值最小化或最小值最大化问题。
- 问题具有单调性,即当答案增大或减小时,问题的可行性会呈现单调变化。
- 直接求解问题较为复杂,但可以通过给定答案快速验证其可行性。
算法步骤
- 确定搜索范围:根据问题的性质,确定答案的可能范围
[left, right]。 - 二分查找:
- 计算中间值
mid = left + (right - left) / 2。 - 检查
mid是否满足条件(即验证可行性)。 - 如果满足条件,尝试寻找更优的解(缩小右边界
right = mid)。 - 如果不满足条件,调整左边界
left = mid + 1。
- 计算中间值
- 终止条件:当
left和right相遇时,找到最优解。
代码模板
以下是二分答案的通用代码模板(以 C++ 为例):
int binarySearchAnswer(int left, int right) {
while (left < right) {
int mid = left + (right - left) / 2;
if (check(mid)) { // 检查 mid 是否满足条件
right = mid; // 满足条件,尝试更小的值
} else {
left = mid + 1; // 不满足条件,尝试更大的值
}
}
return left; // 返回最优解
}
bool check(int mid) {
// 根据问题实现具体的检查逻辑
// 返回 true 或 false
}
示例问题
问题:分割数组的最大值
给定一个数组 nums 和一个整数 k,将数组分成 k 个连续子数组,使得每个子数组的和的最大值最小化。
解题思路
- 确定搜索范围:
- 最小可能值:数组中的最大值(每个子数组至少包含一个元素)。
- 最大可能值:数组的总和(整个数组作为一个子数组)。
- 二分查找:
- 对于每个
mid,检查是否可以将数组分成k个子数组,且每个子数组的和不超过mid。
- 对于每个
- 验证函数:
- 遍历数组,累加元素,当累加和超过
mid时,分割一个新的子数组。 - 如果分割的子数组数量不超过
k,则mid可行。
- 遍历数组,累加元素,当累加和超过
代码实现
int splitArray(vector<int>& nums, int k) {
int left = *max_element(nums.begin(), nums.end());
int right = accumulate(nums.begin(), nums.end(), 0);
while (left < right) {
int mid = left + (right - left) / 2;
if (canSplit(nums, k, mid)) {
right = mid;
} else {
left = mid + 1;
}
}
return left;
}
bool canSplit(vector<int>& nums, int k, int maxSum) {
int sum = 0, count = 1;
for (int num : nums) {
sum += num;
if (sum > maxSum) {
sum = num;
count++;
if (count > k) return false;
}
}
return true;
}
总结
二分答案的关键在于:
- 确定答案的搜索范围。
- 实现一个高效的验证函数
check(mid)。 - 通过二分查找快速缩小范围,找到最优解。
通过练习经典问题(如分割数组、最小化最大值等),可以更好地掌握这一技巧。
浙公网安备 33010602011771号