题目链接:
一、本题为什么能想到利用二分解决?
\(1.\) 有单调性
提高伐木机的高度,显然地,得到的木头会减少。
同样地,放低得到的木头会增多。
而正因为答案有单调性,所以我们可以使用二分。
\(2.\) 数据范围大
如果采用暴力枚举,时间复杂度为 \(O(n \cdot m)\) 会超时。用二分优化后时间复杂度约为 \(O(n \cdot log_2m)\) 最坏时间复杂度约为 \(3e^7\) 可以过。
二、二分答案
解题的时候往往会考虑枚举答案然后检验枚举的值是否正确。若满足单调性,则满足使用二分法的条件。把这里的枚举换成二分,就变成了「二分答案」。——OI Wiki
二分答案题的几个特征:
(1)求最大/最小值;
(2)答案离散(答案有有限种可能);
(3)容易判断答案是否正确
二分答案题的做法即是:
(1)确定答案区间;
(2)在保证答案在区间内的前提下,逐步缩小区间;
(3)当区间缩小到仅包含一个可能解时,该可能解即为答案。
这里主要要注意第(2)点,就是如何缩小区间。这是二分的精华所在,也是二分搜索程序如此难写的原因。
在本题中 \(\rm check\) 函数的作用是计算木材的总长度并判断是否满足需求。如果满足需求则查找是否有小的解(指砍树高度更高)否则向树高更低的区间查找。
下面给出 \(AC\) 代码。
#include <bits/stdc++.h>
using namespace std;
using LL = long long;
const int N = 1e6 + 5;
int a[N];
bool check(int a[], int x, int n, int m) {
LL sum = 0;
for (int i = 0; i < n; i++) {
if (a[i] > x) sum += a[i] - x;
}
return sum >= m;
}//返回true表明:x代表的高度满足题意
int main()
{
int n, m;
scanf("%d%d", &n, &m);
for (int i = 0; i < n; i++) scanf("%d", &a[i]);
sort(a, a + n);
int l = 0, r = a[n-1]-1;//在[0,最高的树高)进行枚举
while (l < r) {
int mid = l + r + 1 >> 1;
if (check(a, mid, n, m)) l = mid;//满足题意则尽可能抬高锯子的高度
else r = mid - 1;//否则说明木材量还不够,需要降低锯子的高度
}
printf("%d", l);
return 0;
}
标签:EKO,二分,P1873,int,mid,枚举,答案,区间,2011
From: https://www.cnblogs.com/pangyou3s/p/18016364