二分训练(东北林业大学)

前言:

  老师要求写的训练题,都是有关二分的。

正文:

个人二分模板:

  由于二分有很多种写法,这里提供我个人用的比较多的。

//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,不然会超时(在这卡了将近一小时了)。

后记:

  二分一定要注意处理边界问题,根据不同情况使用不同模板,但实战中还是根据实际情况来决定写法。

  • 10
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值