二分查找与二分答案(Acwing)

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木材加工

P2440 木材加工 - 洛谷

思考逻辑

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路标设置

P3853 [TJOI2007] 路标设置 - 洛谷

思考逻辑

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;
}

    

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值