前言:
老师要求写的训练题,都是有关二分的。
正文:
个人二分模板:
由于二分有很多种写法,这里提供我个人用的比较多的。
//1.如果有x找到x,否则找到一个最接近x比x大的数
int l = 1, r = n;
while (r > l)
{
int mid = l + r >> 1;
if (a[mid] >= x) r = mid;
else l = mid + 1;
}
//一定是在l=r时退出循环
//如果数组a中包含x这个数那么a[l]==x
//如果数组a中不包含x这个数,但是a[1]<x<a[n],就是x在数组a的范围之内,那么a[l]是数组a中第一个大于x的数
//如果x<a[1]比最小的数还小,那么a[l]==a[1],也就是整个数组中最接近x的数
//如果x>a[n]比最大的数还大,那么a[l]==a[n],也就是整个数组中最接近x的数
//2.如果有x找到x,否则找到一个最接近x比x小的数
int l = 1, r = n;
while (r > l)
{
int mid = l + r + 1 >> 1;
if (c[mid] <= x) l = mid;
else r = mid - 1;
}
//一定是在l=r时退出循环
//如果数组c中包含x这个数那么a[l]==x
//如果数组c中不包含x这个数,但是a[1]<x<a[n],就是x在数组a的范围之内,那么a[l]是数组a中最后一个小于x的数
//如果x<c[1]比最小的数还小,那么c[l]==c[1],也就是整个数组中最接近x的数
//如果x>c[n]比最大的数还大,那么c[l]==c[n],也就是整个数组中最接近x的数
题目:
Problem:A 砍伐树木-二分
#include<bits/stdc++.h>
using namespace std;
const int N=1e6+5;
typedef long long ll;
ll wood[N];
ll n,m;
ll work(int x){
ll ans=0;
for(int i=1;i<=n;i++){
if(x<wood[i])ans+=wood[i]-x;
}
return ans;
}
int main(){
ll mx=0;
cin>>n>>m;
for(int i=1;i<=n;i++){
cin>>wood[i];
mx=max(mx,wood[i]);
}
ll l=0,r=mx,mid;
while(l<r){
mid=l+r+1>>1;
if(work(mid)>=m)l=mid;
else r=mid-1;
// cout<<l<<" "<<r<<endl;
}
cout<<r<<endl;
return 0;
}
Problem:B 切绳子实数版-二分
#include<bits/stdc++.h>
using namespace std;
const int N=1e5+5;
double a[N];
int b[N];
int n,k;
int check(int len){
int num=0;
for(int i=1;i<=n;i++)
num+=b[i]/len;
if(num>=k)return 1;
else return 0;
}
int main(){
ios::sync_with_stdio(0);
cin>>n>>k;
for(int i=1;i<=n;i++){
cin>>a[i];
b[i]=a[i]*100;
}
int l=1,r=10000000,mid,ans=0;
while(l<=r){
mid=(l+r)/2;
if(mid==0)break;
if(check(mid))l=mid+1,ans=mid;
else r=mid-1;
}
printf("%.2f\n",ans/100.0);
return 0;
}
Problem:C 数列分段-二分
#include<bits/stdc++.h>
using namespace std;
const int N=1e6+5;
typedef long long ll;
int a[N];
int n,m;
bool check(ll x){
int cnt1=0,cnt2=0;
for(int i=1;i<=n;i++){
cnt1+=a[i];
if(cnt1>x||i==n){
cnt2++;
cnt1=a[i];
}
else continue;
}
//cout<<cnt2<<endl;
if(cnt2>m)return true;
return false;
}
int main(){
ll sum=0;
int mx=0;;
cin>>n>>m;
for(int i=1;i<=n;i++){
cin>>a[i];
sum+=a[i];
mx=max(mx,a[i]);
}
ll l=mx,r=sum,mid;
while(l<=r){
mid=l+r>>1;
if(check(mid))l=mid+1;
else r=mid-1;
// cout<<l<<" "<<r<<endl;
}
cout<<l<<endl;
return 0;
}
Problem:D 卖古董-DP-二分
#include<bits/stdc++.h>
using namespace std;
const int N=1e6+5;
typedef long long ll;
int a[N];
int t,n,m;
bool check(ll x){
int cnt1=0,cnt2=0;
for(int i=1;i<=n;i++){
cnt1+=a[i];
if(cnt1>x||i==n){
cnt2++;
cnt1=a[i];
}
else continue;
}
//cout<<cnt2<<endl;
if(cnt2>m)return true;
return false;
}
int main(){
cin>>t;
while(t--){
ll sum=0;
int mx=0;;
cin>>n>>m;
for(int i=1;i<=n;i++){
cin>>a[i];
sum+=a[i];
mx=max(mx,a[i]);
}
ll l=mx,r=sum,mid;
while(l<=r){
mid=l+r>>1;
if(check(mid))l=mid+1;
else r=mid-1;
// cout<<l<<" "<<r<<endl;
}
cout<<l<<endl;
}
return 0;
}
上题的多组数据版。
Problem:E 小清新的二分查找之旅
#include<bits/stdc++.h>
using namespace std;
const int N=1e6+5;
typedef long long ll;
int a[N];
int main(){
ios::sync_with_stdio(0);
int n,q,mx=0;
while(~scanf("%d%d",&n,&q)){
for(int i=1;i<=n;i++){
scanf("%d",&a[i]);
}
while(q--){
int x,flag=0;
scanf("%d",&x);
ll l=1,r=n,mid;
while(l<=r){
mid=l+r>>1;
if(a[mid]<x)
l=mid+1;
else if(a[mid]>x)
r=mid-1;
else if(a[mid]==x){
flag=1;
break;
}
// cout<<l<<" "<<r<<endl;
}
if(flag==0)printf("YES\n");
else printf("no\n");
}}
return 0;
}
注意输入数据很多,建议使用scanf,不然会超时(在这卡了将近一小时了)。
后记:
二分一定要注意处理边界问题,根据不同情况使用不同模板,但实战中还是根据实际情况来决定写法。