二分查找 & 二分答案 ,课后练习题答案。

二分查找&二分答案,详细讲解博客地址:https://blog.csdn.net/Mr_dimple/article/details/114656142

二分查找:

1、数的范围

#include<iostream>
using namespace std;

const int N=100010;
int a[N],q,x,n;

int main(){
	cin>>n>>q;
	for(int i=0;i<n;i++) cin>>a[i];
	
	while(q--)
	{
		cin>>x;
		int l=0,r=n-1;
		while(l<r)	//找最左边的边界   注意没有 "=" 
		{
			int mid=l+r>>1;
			if(a[mid]>=x) r=mid; //判断条件:在右边的话就往左来 
			else l=mid+1;
		}
		if(a[l]!=x){
			cout<<"-1 -1"<<endl;
			continue;
		}
		cout<<l<<" ";
		
		l=0,r=n-1;
		while(l<r)	//找最右边的边界 
		{
			int mid=l+r+1>>1;
			if(a[mid]<=x) l=mid; //在左边的话就往右来 
			else r=mid-1;
		}
		cout<<l<<endl;
	}
	return 0;
} 

2、砍树

#include<iostream>
using namespace std;

const int N=1e6+10;
long long a[N],n,m,maxa,sum;

int main(){
	cin>>n>>m;
	for(int i=1;i<=n;i++){
		cin>>a[i];
		if(a[i]>maxa) maxa=a[i]; //找一下可能的最大高度 
	}
	long long l=0,r=maxa;
	while(l<r)	//查找最高的满足情况的高度 
	{
		long long mid=l+r+1>>1;
		sum=0;
		for(int i=1;i<=n;i++){
			if(a[i]>mid) sum+=a[i]-mid;
		}
		if(sum>=m) l=mid;	//低了
		else r=mid-1;
	}
	cout<<l<<endl;	//一定有解 
	
	return 0;
} 

3、数的三次方根

#include<iostream>
using namespace std;

double n;
int flag=1;

int main(){
	cin>>n;
	
	if(n<0) flag=-1,n=-n; //负数特判 
	
	double l=0,r=n;
	if(n<1) r=1;	//小数特判,因为小数乘小数越乘越小,而小数的三次根是比自身大的,故无法解小数的三次根 
					//又考虑到小数的三次根肯定小于1,则将r设为1 
	
	while(r-l>1e-8)
	{
		double mid=(l+r)/2;
		if(mid*mid*mid>=n) r=mid;
		else l=mid; 
	}
	printf("%.6f",l*flag);
	return 0;
} 

4、一元三次方程求解

#include<iostream>
using namespace std;

double a,b,c,d;

int search_1(double l,double r)
{
	while(r-l>1e-5)
	{
		double mid=(l+r)/2;
		if(a*mid*mid*mid+b*mid*mid+c*mid+d<=0) l=mid;
		else r=mid;
	}
	printf("%.2f ",l);
}
int search_2(double l,double r)
{
	while(r-l>1e-5)
	{
		double mid=(l+r)/2;
		if(a*mid*mid*mid+b*mid*mid+c*mid+d>=0) l=mid;
		else r=mid;
	}
	printf("%.2f ",l);
}
int main(){
	cin>>a>>b>>c>>d;
	//注意根与根之差的绝对值>=1,故可以遍历每个点,看是否根在这个点附近,如果在的话,二分查找 
	for(double i=-101;i<=101;i++){
		if(a*i*i*i+b*i*i+c*i+d==0){
			printf("%.2f ",i);
		}
		else if(a*i*i*i+b*i*i+c*i+d<0&&a*(i+1)*(i+1)*(i+1)+b*(i+1)*(i+1)+c*(i+1)+d>0){
			search_1(i,i+1); 
		}
		else if(a*i*i*i+b*i*i+c*i+d>0&&a*(i+1)*(i+1)*(i+1)+b*(i+1)*(i+1)+c*(i+1)+d<0){
			search_2(i,i+1);
		}
	}
	return 0;
} 

二分答案:

1、进击的奶牛

#include<iostream>
#include<algorithm>
using namespace std;

const int N=100010;
int a[N],n,m,maxa;

int check(int mid)
{
	int i=1,j=1,cnt=0; //第一个格子必放牛,才能使后面的间距尽可能大,从而保证最短距离mid最大化 
	while(j<n){
		j++;
		if(a[j]-a[i]>=mid){ //保证间距大于等于最短距离mid,才能把牛放进去 
			cnt++,i=j;
		}
	}
	if(cnt+1>=m) return 1; //放进的牛个数大于m,都能保证间距大于最短距离mid,那放进去m个,肯定还是大于最短距离mid 
	return 0;
}

int main(){
	cin>>n>>m;
	for(int i=1;i<=n;i++){
		cin>>a[i];
		if(a[i]>maxa) maxa=a[i];
	}
	sort(a+1,a+n+1);	//排序勿忘 
	
	int l=0,r=maxa;
	while(l<r)
	{
		int mid=l+r+1>>1;
		if(check(mid)) l=mid;
		else r=mid-1;
	}
	cout<<l;
	return 0;
} 

2、路标设置

#include<iostream>
using namespace std;

const int N=100010;
long long a[N],n,m,len,x;

int check(int mid)
{
	int cnt=0,ans=0;
	for(int i=1;i<=len;i++){
		cnt++;
		if(a[i]==1){
			cnt=0;
			continue;
		}
		if(cnt+1>mid) ans++,cnt=0; //如果两路标间距大于最大距离mid了,即,不能保证" 区间距离小于最大距离mid或相等 ",那就增设 
	}
	if(ans<=m) return 1; //增设不到m个就能满足 "间距小于最大距离或相等" ,那增设m个肯定也满足 
	return 0;
}

int main(){
	cin>>len>>n>>m;
	for(int i=1;i<=n;i++){
		cin>>x;
		a[x]=1;
	}
	a[0]=1,a[len]=1;
	
	int l=0,r=len;
	while(l<r)
	{
		int mid=l+r>>1;
		if(check(mid)) r=mid;
		else l=mid+1;
	}
	cout<<l;
	return 0;
} 

3、最佳牛围栏

#include<iostream>
using namespace std;

const int N=100010;
int a[N],n,m;
double sum[N];
 
int check(double mid)
{
	//难点:如何判断一个长度大于等于m的区间的平均值是否大于mid。
	//求平均值转化:可将区间的每个值减掉mid,判断该区间的和是否大于等于0,如果是,那么平均值就大于等于mid 
	for(int i=1;i<=n;i++){ //每个值减掉mid(平均值)后取前缀 
		sum[i]=sum[i-1]+a[i]-mid;
	}
	
	//难点:是否需要遍历每一个长度大于等于m的区间 
	//技巧:取区间的左边界最小的前缀和,用右边界减最小左边界判断是否大于等于0,这样就能找到区间长度大于等于m且最大的区间平均值了 
	double mina=1e5;
	for(int i=0,j=m;j<=n;i++,j++){ //保证区间长度为m,依次往后移 
		if(sum[i]<mina) mina=sum[i];
		if(sum[j]-mina>=0) return 1; //如果该右边界减最小左边界(即区间和)大于等于0,说明区间平均值大于mid 
	}
	return 0;
}

int main(){
	cin>>n>>m;
	for(int i=1;i<=n;i++){
		cin>>a[i];
	}
	
	double l=0,r=2000;
	while(r-l>1e-5)
	{
		double mid=(l+r)/2;
		if(check(mid)) l=mid;
		else r=mid;
	}
	printf("%d",int(r*1000)); //最终输出的是r,而不是l,因为题目让求的是最大值。这一点和整数二分不同! 
	return 0;
}

4、kotori的设备
代更。。。

有哪里不懂的欢迎留言讨论。

有问题的话还请大佬们不吝赐教。

  • 22
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 11
    评论
评论 11
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值