1.整数二分模版
bool check(int x) {/* ... */} // 检查x是否满足某种性质
// 区间[l, r]被划分成[l, mid]和[mid + 1, r]时使用:
int bsearch_1(int l, int r)
{
while (l < r)
{
int mid = l + r >> 1;
if (check(mid)) r = mid; // check()判断mid是否满足性质
else l = mid + 1;
}
return l;
}
// 区间[l, r]被划分成[l, mid - 1]和[mid, r]时使用:
int bsearch_2(int l, int r)
{
while (l < r)
{
int mid = l + r + 1 >> 1;
if (check(mid)) l = mid;
else r = mid - 1;
}
return l;
}
例题1.数的范围


代码
#include <iostream>
using namespace std;
const int N=1e6+10;
int n,m;
int q[N];
int main()
{
scanf("%d%d",&n,&m);
for(int i=0;i<n;i++) scanf("%d",&q[i]);
while(m--)
{
int x;
scanf("%d",&x);
int l=0,r=n-1;
while(l<r)
{
int mid=l+r>>1;
if(q[mid]>=x) r=mid;
else l=mid+1;
}
if(q[l]!=x) cout<<"-1 -1"<<endl;
else
{
cout<<l<<' ';
int l=0,r=n-1;
while(l<r)
{
int mid=l+r+1>>1;
if(q[mid]<=x) l=mid;
else r=mid-1;
}
cout<<l<<endl;
}
}
return 0;
}
例题2.查找

代码
#include<bits/stdc++.h>
using namespace std;
const int N=1e6+10;
int n;
int q[N];
vector<int> vt;
void ef(int x)
{
int l=1,r=n;
while(l<r)
{
int mid=(l+r)>>1;
if(q[mid]>=x) r=mid;
else l=mid+1;
}
if(q[l]==x) vt.push_back(l);
else vt.push_back(-1);
}
int main()
{
int m;
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
{
scanf("%d",&q[i]);
}
while(m--)
{
int x;
scanf("%d",&x);
ef(x);
}
for(auto&ele:vt)
{
printf("%d ",ele);
}
}
2.二分答案
基本思路
例题1砍树
P1873 [COCI 2011/2012 #5] EKO / 砍树 - 洛谷
思考逻辑
1.答案范围:锯片的高度范围[0,max(a)-1],锯片高度为0表示每棵树都能被完全砍伐,锯片高度不能是每棵树中最高的一个,这样做获取的木材的总长度为0,而木材总长度要求>=1,所有设置锯片的高度范围的上界值为每棵树中最高的一个-1
2.单调性:锯片高度越高,获取的木材总长度约小;
锯片高度越低,获取的木材总长度约大;
3.写check函数,当锯片的高度固定时,要判断此时获取的木材总长度是否>=M
4.判断使用哪个二分模版,以本题为例,如果当前的锯片的高度满足check函数,那么比当前的锯片的高度低的答案也满足,即mid=(l+r+1)>>1;l=mid;else r=mid-1;
代码
#include<bits/stdc++.h>
using namespace std;
const int N=1e6+10;
#define int long long
int a[N];
int n,m;
bool check(int a[],int x)
{
int sum=0;
for(int i=1;i<=n;i++)
{
if(a[i]>x)
{
a[i]=a[i]-x;
}
else if(a[i]<=x)
{
a[i]=0;
}
}
for(int i=1;i<=n;i++)
{
sum+=a[i];
}
if(sum>=m)
{
return true;
}
}
signed main()
{
cin>>n>>m;
for(int i=1;i<=n;i++)
{
cin>>a[i];
}
sort(a+1,a+n+1);
int l=1,r=a[n]-1;
int ans;
while(l<r)
{
int mid=(l+r+1)>>1;
if(check(a,mid))
{
l=mid;
ans=mid;
}
else r=mid-1;
}
cout<<l<<endl;
}
例题2木材加工
思考逻辑
1.答案范围:长度均为 l 的小段木头,l的范围[1,max(a)],l最小为1,不能为0,最大值为max(a),表示最多切割成一段,符合题目要求
2.单调性:l越大,小段的数量越少;
l越小,小段的数量越多;
3.写check函数,当l固定时,要判断此时的小段数量是否>=k
4.判断使用哪个二分模版,以本题为例,如果l满足check函数,那么比当前l小的答案也满足,即mid=(l+r+1)>>1;l=mid;else r=mid-1;
代码
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N=1e5+10;
int a[N];
int n,k;
bool check(int a[],int x)
{
if(x==0) return false;
int total=0;
for(int i=1;i<=n;i++)
{
total+=a[i]/x;
}
return total>=k;
}
signed main()
{
cin>>n>>k;
for(int i=1;i<=n;i++)
{
cin>>a[i];
}
sort(a+1,a+n+1);
int l=1,r=a[n];
int ans=0;
while(l<r)
{
int mid=(l+r+1)>>1;
if(check(a,mid))
{
ans=mid;
l=mid;
}
else
r=mid-1;
}
cout<<ans<<endl;
}
例题3跳石头
P2678 [NOIP 2015 提高组] 跳石头 - 洛谷
思考逻辑
1.答案范围:最短跳跃距离的最大值范围[1,L],
最小值是1(不移走任何岩石,岩石之间至少相距1)。
最大值是L(移走所有中间岩石,直接从起点跳到终点)。
2.单调性:最短跳跃距离的最大值越大,满足条件(所有相邻石头之间的距离>=最短跳跃距离的最大值)的难度增加-需要移动更多的石头;
最短跳跃距离的最大值越小,满足条件(所有相邻石头之间的距离>=最短跳跃距离的最大值)的难度增加-需要移动更少的石头;
3.写check函数,当最短跳跃距离的最大值固定时,要判断此时需要移动的石头个数否<=M
4.判断使用哪个二分模版,以本题为例,如果当前的最短跳跃距离的最大值满足check函数,那么比当前的最短跳跃距离的最大值低的答案也满足,即mid=(l+r+1)>>1;l=mid;else r=mid-1;
代码
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N=50010;
int a[N];
int L,n,m;
bool check(int a[],int x)
{
int cnt=0;
int p=0;
for(int i=1;i<=n;i++)
{
if(a[i]-p>=x)
{
p=a[i];
}
else
{
cnt++;
}
}
if(L-a[n]<x)
{
cnt++;
}
return cnt<=m;
}
signed main()
{
cin>>L>>n>>m;
for(int i=1;i<=n;i++)
{
cin>>a[i];
}
int l=1,r=L;
int ans=0;
while(l<r)
{
int mid=(l+r+1)>>1;
if(check(a,mid))
{
ans=mid;
l=mid;
}
else r=mid-1;
}
cout<<ans<<endl;
}
例题4路标设置
思考逻辑
1.答案范围:相邻路标的最大距离定义为该公路的“空旷指数”。空旷指数的范围[1,L],
最小值是1(增设足够多路标,相邻路标之间的距离为1)。
最大值是L(只有起点和终点两个路标,相邻距离为L)。
2.单调性:空旷指数越大,-减少路标
空旷指数越小,-增加路标
3.写check函数,当空旷指数固定时,要判断此时增设的路标数量是否<=k
4.判断使用哪个二分模版,以本题为例,如果当前的空旷指数满足check函数,那么比当前的空旷指数大的答案也满足,即mid=(l+r)>>1;r=mid;else l=mid+1;
代码
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N=1e7+10;
int L,n,k;
int a[N];
bool check(int a[],int x)
{
int ans=0;
for(int i=1;i<=n;i++)
{
int dist=a[i]-a[i-1];
ans+=(dist-1)/x;
}
int dist=L-a[n];
ans+=(dist-1)/x;
return ans<=k;
}
signed main()
{
cin>>L>>n>>k;
for(int i=1;i<=n;i++)
{
cin>>a[i];
}
int l=0,r=L;
int ans=0;
while(l<r)
{
int mid=(l+r)>>1;
if(check(a,mid))
{
ans=mid;
r=mid;
}
else l=mid+1;
}
cout<<ans<<endl;
return 0;
}
&spm=1001.2101.3001.5002&articleId=146600838&d=1&t=3&u=2313498b08a14b309265e5e2e7b255b9)
1748

被折叠的 条评论
为什么被折叠?



