二分查找算法

二分YYDS;

// 区间[l, r]被划分成[l, mid]和[mid + 1, r]时使用:
int bsearch_1(int l, int r)
{
    while (l < r)
    {
        int mid = l + r >> 1;
        if ("条件") r = 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 ("条件") l = mid;
        else r = mid - 1;
    }
    return l;
}

由于l+r>>1 是向下取整

当区间只有两个数的时候,mid=1,bsearch_2造成死循环。

--------------------------------------------------------------------------------------------------------------------分割线

最近算是重新回顾了一下二分这个算法吧。

然后我发现我之前学的二分算是学了个寂寞,这次温故而知新,感觉收获还是蛮大的,所以,我来更新博客了。

上次的话,咱们是就直接放了模板在这,这次咱们来看看为啥要用两个板子。

一个板子不行吗?

先来放上练习题:

【深基13.例1】查找 - 洛谷

#include<bits/stdc++.h>
using namespace std;

const int N = 1000010;
typedef long long LL;
int n,m;

LL a[N];

LL bsearch(LL l,LL r,LL x)
{	
	while(l<r)
	{
		LL mid = l+r>>1;
		if(a[mid] >= x) r = mid;
		else l = mid+1; 
	}
	return l;
	
}

int main()
{
	ios::sync_with_stdio(false);
	cin.tie(0);
	
	cin>>n>>m;
	for(int i=1;i<=n;i++)cin>>a[i];
	
	while(m--)
	{
		LL x;
		cin>>x;
		LL t = bsearch(1,n,x);
		
		if(a[t]!=x)	cout<<-1<<" ";
		else	cout<<t<<" ";		
	}
	return 0;
} 

A-B 数对 - 洛谷(这道题我貌似是用哈希表写的[doge])

#include<bits/stdc++.h>
using namespace std;

const int N = 200010;

typedef long long LL;

LL n,c;
LL a[N],idx;
bool st[N];
LL res;

unordered_map<LL,LL>heap;

int main()
{
	scanf("%lld%lld",&n,&c);
	
	for(int i=1;i<=n;i++)
	{
		scanf("%lld",&a[i]);
		heap[a[i]]++;
		a[i] -=c;
	}
		
	for(LL i =1;i<=n;i++)
	{
		res +=heap[a[i]];
	}
	cout<<res<<endl;
	return 0;
}

[COCI 2011/2012 #5] EKO / 砍树 - 洛谷

#include<bits/stdc++.h>
using namespace std;

const int N = 1000010;

typedef long long LL;

LL n,m;
LL a[N];

LL sum(LL y)
{
	LL res=0;
	for(LL i =1;i<=n;i++)
	{
		LL x = a[i];
		if(x>y) res += x - y;
	}
	return res;
}

LL bsearch(LL l,LL r,LL x)
{
	while(l<r)
	{
		LL mid = l + r + 1>>1;
		
		LL t = sum(mid);
		if(t == x)return mid;
		else if(t>x) l = mid;
		else r = mid - 1;
	}
}

int main()
{
	scanf("%d%d",&n,&m);
	
	for(LL i=1;i<=n;i++)scanf("%d",&a[i]);
	sort(a+1,a+n+1);
	cout<<bsearch(a[1],a[n],m);
	
	return 0;
} 

木材加工 - 洛谷

#include<bits/stdc++.h>
using namespace std;

const int N = 1000010;
typedef long long LL;

int n,k;
int a[N];
int MX;
int sum(int mid)
{
	int res =0;
	for(int i=1;i<=n;i++) res += a[i]/mid;
	return res;
}

int search(int l,int r)
{
	while(l<r)
	{
		int mid = l + r + 1>>1;
		if(sum(mid)>=k) l = mid;
		else r = mid -1;
	}
	return l;
}

int main()
{
	ios::sync_with_stdio(false);
	cin.tie(0);
	
	cin>>n>>k;
	for(int i=1;i<=n;i++)
	{
		cin>>a[i];
		MX = max(MX,a[i]);
	}
	if(MX ==0)cout<<0<<endl;
	else cout<<search(0,MX)<<endl;
	return 0;
}

咱们来看看这两个板子的区别:

bsearch_1板子:将区间划分成了 [ l , mid ] 和 [ mid + 1 , r];

bsearch_2板子:将区间划分成了 [ l ,  mid - 1 ] 和 [ mid , r];

垂死梦中惊坐起,再看俩板我可以。

我们的"条件"是查找出满足我们条件的答案的。

但是如果当答案不为一时呢,这时候两个板子的作用就展现出来了。

 我们来看看这串数字,然后我们来找出 2 这个数的位置。

我们用第一个板子来找,可以惊奇的发现,我们找到的是第一个 2 出现的位置,而用第二个板子可以找到最后一个 2 出现的位置。

 这就是这两个板子的巧妙之处,具体的东西还需要大家亲手去写一下,模拟一下过程的。

----------------------------------------------------------------------------------------------------- -这是一条分割线

我又来了,最近又学习了一遍二分,只能说二分YYDS了;

其实上面的东西简单来说就是,二分查找符合条件的两个边界;

也就是左右边界,这次学了一个新的二分写法,大家感兴趣的可以看看;

这次我把之前放的练习题的代码也放上了(不知道为啥浏览好少);思路啥的我都不记得了;

[NOIP2015 提高组] 跳石头 - 洛谷

这个题的话,我们就二分答案,然后枚举一下,查看当前答案是否合法,(枚举的过程我用的方法可能有点傻),然后我们根据结果进行二分的边界选择;

#include<iostream>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<unordered_map>
#define x first
#define y second 
#define _for(i,s,t) for(int i = (s);i <=t ; i++)
#define FAST ios::sync_with_stdio(false); cin.tie(0);cout.tie(0);

using namespace std;

typedef unsigned long long ULL;
typedef long long LL;
typedef pair<int,int> PII;

const int N = 50010;

int k,m,n;

LL a[N],s[N];

bool check(LL num)
{
	LL ant = 0, i = 1;

	while(i <= n)
	{
		if(s[i] < num)
		{
			LL sa = s[i],j = i + 1; 
			while(sa < num && j <= n + 1)
			{
				ant++;
				sa += s[j];
				j++;	 
			}
			i = j - 1;
		}
		i ++;
	}
	if(ant > m) return false;
	
	return true;
}


int main()
{
	scanf("%d%d%d",&k,&n,&m);
	
	for(int i =1 ;i<= n;i++)
	{
		scanf("%lld",&a[i]);
		s[i] = a[i] - a[i-1];
	}
	s[m+1] = k - a[n];


	LL  l = 0 , r = k + 1;
	
	while(l  + 1 < r)
	{
		
		LL mid = l + r  >> 1;
		if(check(mid)) l = mid;
		else r = mid ;
	}
	
	printf("%lld\n",l);
	
	return 0;
} 

 [TJOI2007]路标设置 - 洛谷

本题和上一题基本一样,我们二分答案,对于每一个路灯区间,我们查看使其合法化需要多少个路灯,最后求和题目给出的对比,然后判断二分边界,注意本题查找的是左边界。

#include<iostream>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<unordered_map>
#define x first
#define y second 
#define _for(i,s,t) for(int i = (s);i <=t ; i++)
#define FAST ios::sync_with_stdio(false); cin.tie(0);cout.tie(0);

using namespace std;

typedef unsigned long long ULL;
typedef long long LL;
typedef pair<int,int> PII;

const int N = 100010;


LL ll,n,k;

int a[N],s[N],idx;

bool check(LL num)
{
	LL ant = 0;
	_for(i,1,idx)
	{
		if(s[i] > num)
		{
			ant += (s[i] / num) - 1;
			if(s[i] % num) ant ++;
		}
	}
	if(ant > k) return false;
	
	return true;
} 

int main()
{
	scanf("%lld%d%d",&ll,&n,&k);
	
	_for(i,1,n)	scanf("%lld",&a[i]);	
		
	_for(i,2,n) s[++idx] = a[i] - a[i-1];

	LL l = 0, r = ll;
	
	while(l < r)
	{
		LL mid = l + r >> 1;
		if(check(mid))r = mid;
		else l = mid + 1;
	}
	
	printf("%lld\n",l);
	
	return 0; 
}
/*
101 5 1
0 38 69 83 101
31
*/
 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

是饿梦啊

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值