
先就这么写,写完过后,举例3个数据,分别假设这三个数据是答案
当既不会出现死循环,又能输出正确答案,那就是对的

eg.找到4是一个答案,l=mid+1。如果5不是答案 和 如果5是答案, 看看能不能退出循环
二分查找的应用----二分答案(猜答案)
步骤:1.确定题目是二分的题目 2.确定答案的范围 3.假定中间位置是否可行 4.通过判断不断二分缩小范围
题型:
1.求最值 ---->可能用二分
2.最小化最大值 3.最大化最小值 ---->一定用二分
求最值
例题1:
P1873 COCI 2011/2012 #5 EKO / 砍树 - 洛谷
时间复杂度 O(n*lg(r-l))
cpp
#include<iostream>
#include<cmath>
using namespace std;
int tree[1000005]; //每棵树的高度
int N;long M; //树的个数,所需木材(1≤M≤2×10^9,刚好int)
bool check(int mid, int maxh)
{
//cout << "mid=" << mid << " "; 调试
long long sum = 0; //可能超过int
// for (int i = 1; i <= maxh; i++) { 数组和下标都弄混淆了
// if (mid < h[i]) {
// sum += h[i] - mid;
// }
// }
//cout << "sum=" << sum << ",M=" << M << " ";
for (int i = 1; i <= N; i++)
{
if (mid < tree[i])
{
sum += tree[i] - mid;
}
}
if (sum >= M) return true;
else return false;
}
int main()
{
cin >> N >> M;
int maxh = 0;
for (int i = 1; i <= N; i++) {
cin >> tree[i];
maxh = max(maxh, tree[i]);
}
//二分区间
int l = 0, r = maxh;
int ans = 0;
while (l <= r) {
int mid = (l + r) / 2;
if (check(mid,maxh)) { //若合法,因为找最大值,所以先记录答案,再往更大的方向找
ans = max(ans, mid);
l = mid + 1;
}
else {
r = mid - 1;
}
}
cout << ans;
}
最大化最小值和最小化最大值
最小化最大值 在5个班里面最高的个子中挑一个最矮的
最大化最小值 在5个班里面最矮的个子中挑一个最高的
最大化最小值题目: 某个情景,求一个最大化最小值
二分最小值
l,r mid--->最小值---->反推题目给出的另一个条件-----
(如果不满足,说明还不够小,往小方向取;如果满足,再增大(最大化最小值),看能不能更大化)
最大化最小值题目:满足 最大化,不满足减小化
2.例题P2678 NOIP 2015 提高组 跳石头 - 洛谷


最大化最小值题目:满足则最大化,不满足则 减小化 (注意这点)
Q1:什么叫做最短跳跃距离

2
Q2:什么叫做最短跳跃距离最大化
题目允许你移走岩石,这样会改变岩石的分布,于是就会得到新的最短距离。
移走一个时,移走两个时(<=m) ... 求出一个最短距离最大的数。
Q3: 确定跳跃距离的范围
(每个岩石都是挨着的) 1 ~~ L (直接跳到终点) mid=(1+L)/2 然后得找大于等于mid的岩石
如果两个岩石之间的距离大于mid,不动。如果AB距离小于了mid,要移动(因为mid一定是最短移动距离)
那问题是移动A还是移动B?

1.A是起点,移动B 2.B是终点,移动A 3.A,B在中间,A/B
Q4:如果发现移动的岩石大于了m,那说明mid怎么样?
说明mid取大了,因为小于mid的才会被移动,mid太大了就会导致岩石移的多
最大化最小值题目:满足则最大化,不满足则 减小化

往左走,二分找7。 7还大了,找4,4时刚好移走m 求的是最大化,刚好移动m时是最大化最小值
Q5:怎么去比较两个岩石之间的距离和统计移走岩石数目?

两个指针now,和next。now指向岩石起点,next往右走,判断之间的距离
如果小于mid,cnt++(怎样移走在代码中不用体现出来)。如果大于mid,now才移动到next的位置

时间复杂度 O(nlgL)
cpp
#include<iostream>
using namespace std;
int S, N, M; //距离,起点终点之间的岩石数,至多移走的
int dis[50005]; //统计每个点到起点的距离. 下标N+1
//int res[1000000005]; //答案数组
bool check(int mid)//判断合不合法:即是否移走不多于M块岩石
{
//双指针
int now = 0, next = 0; //两块岩石的起点,终点
int cnt=0; //统计移走的数目
for (next = 1; next <= N + 1; next++) {
if (mid > dis[next] - dis[now]) {
cnt++; //代码中不用体现岩石是怎么移走的
}
else { //距离合法,now可以移动过去了
now = next;
}
if (cnt > M) break;
}
if (cnt <= M) return true;
else return false;
}
int main()
{
cin >> S >> N >> M;
dis[0] = 0; //起点
dis[N + 1] = S; //终点
for (int i=1; i <= N; i++) {
cin >> dis[i];
}
//答案区间1~~S
//二分法
int l = 1, r = S;
int ans = 0;
while (l <= r) {
int mid = (l + r) / 2;
if (check(mid)) {
ans = max(ans, mid); //可能是答案,保存下来
l = mid + 1;
}
else r = mid - 1;
}
cout << ans;
}
总结题目类型:
1.求最值
2.最大化最小值/最小化最大值
3.用于枚举优化---->logn
另外注意:

二分的代码不唯一,四个地方要配合起来,只要不出现死循环就行---->方法:拿三个数据去模拟一下

假设3是答案,推一次。假设4是答案,推一次。假设5是答案,推一次。 保证三次都能退出循环