二分经典例题

本文介绍了三种不同的二分搜索应用:1) 查找数组中特定值的边界;2) 求解最大操作次数使得数列最小值最大化;3) 计算多个序列满足条件的和。讲解了每种应用的思路、代码实现和二分搜索的细节,包括浮点数二分。此外,还提到了二分搜索在字符串和最长上升子序问题中的应用。
摘要由CSDN通过智能技术生成

整数二分:

题目链接:http://oj.daimayuan.top/course/22/problem/59

板子题:

#include<bits/stdc++.h>
using namespace std;
const int N=2e6+10;
int a[N],n,q;

int calc1(int x){//查找第一个小于x的位置
	int L=0,R=n+1;
	while(L+1<R){
		int mid=L+R>>1;
		if(a[mid]<x) L=mid;
		else R=mid;
	}
	return L;
}

int calc2(int x){//查找第一个小于等于x的位置
	int L=0,R=n+1;
	while(L+1<R){
		int mid=L+R>>1;
		if(a[mid]<=x) L=mid;
		else R=mid;
	}
	return L;
}

inline void solve(){
	cin>>n>>q;
	for(int i=1;i<=n;i++) cin>>a[i];
	sort(a+1,a+1+n);
	while(q--){
		int x;cin>>x;
		int x1=calc1(x),x2=calc2(x);
		cout<<x1<<" "<<x2-x1<<" "<<n-x2<<"\n";
	}
}

int main(){
	solve();
}

 题目链接:http://oj.daimayuan.top/course/22/problem/61

题意:给定一个长度为N的数组,以及最大操作次数K,求经过M次操作后使得数列中的最小值最大。

思路:要想使得最小值最大,那么一定是经过了K次操作,这里直接二分答案,通过答案来求解操作数

代码:

#include<bits/stdc++.h>
using namespace std;
const int N=2e6+10;
int a[N],n,k;

bool check(int x){//检查答案是否符合条件
	int ans=0;
	for(int i=1;i<=n;i++)
		ans+=max(0,x-a[i]);
	return ans<=k;
}

int main(){
	cin>>n>>k;
	for(int i=1;i<=n;i++) cin>>a[i];
	int L=0,R=n+1;
	while(L+1<R){//二分答案
		int mid=L+R>>1;
		if(check(mid)) L=mid;
		else R=mid;
	}
	cout<<L;
}

题目链接:http://oj.daimayuan.top/course/22/problem/649

方法: 二分,怎么二分?

因为把所有序列给合并起来,再算和,是一定会TIE的。

如果可以求出每个序列都能满足条件的和,再把所有序列的和加起来,就好了。

 那么我们找出了位置,如何求和呢?

直接采用等差数列的公式进行O(1)求和:n*(a1+an)/2

代码:

#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=2e5+10;
int n,k[N],b[N],m;

int calc(int z){//计算边界L左边小于等于z的个数
	int res=0;
	for(int i=1;i<=n;i++){
		if(z>=b[i]){
		//kx+b=z--->x=(z-b)/k
            int y=(z-b[i])/k[i]+1;//记录个数
		//为什么要加1,是因为每组序列中,都存在x=0的情况
			res+=y;//记录个数
		}
	}
	return res;
}

int sum(int z){
	int res=0;
	for(int i=1;i<=n;i++){
		if(z>=b[i]){
			int y=(z-b[i])/k[i] + 1;//记录项数
			res+=1LL*((b[i]+(y-1)*k[i]+b[i])*y/2);//记录每个序列中的和
			//等差数列求和公式:n*(a1+an)/2
		}
	}
	return res;
}

signed main(){
	cin>>n;
	for(int i=1;i<=n;i++) cin>>k[i]>>b[i];
	cin>>m;
	int L=0,R=1e14;//R尽可能的大
	while(L+1<R){//二分
		int mid=L+R>>1;
		if(calc(mid)<=m) L=mid;
		else R=mid;
	}
	int ans=sum(L)+1LL*((m-calc(L))*(L+1));//最终答案
	cout<<ans;
}

浮点数二分:

这里稍微讲一下:浮点数二分其实和整数二分是差不多的。唯一不同点在于:整数的二分次数是不确定的,而浮点数二分一般是能够算出来的,但是浮点数二分一般二分100次就可以了。 

题目链接:http://oj.daimayuan.top/course/22/problem/648

思路:二分答案。对于方向移动问题,我们可以考虑成区间的问题解答。 

举个例子:a b人在两个不同点,他们要达到目标点,给定两人的速度,问最少能够到达?

我们发现,只要两人跑到路程区间有交集,那么就可以到达,最少时间为跑的慢的人刚好到达目标点的时间。

那么为什么跑的快的人不会超过目标位置呢?

因为跑的快的人到达目标点后,是可以原地踏步的,就相当于在原地跑。(奇怪的知识又增加了Q_Q) 

同理:多人跑目标位置,也是一样的道理。每一次都要改变交集,以保持最优情况

代码:

#include<bits/stdc++.h>
using namespace std;
const int N=2e5+10;
int n;
int pos[N],v[N];

bool check(double x){
	double L=-1e10,R=1e10;//初始区间尽可能大
	for(int i=1;i<=n;i++){
		double l=pos[i]-v[i]*x,r=pos[i]+v[i]*x;//求左右距离
		L=max(L,l);R=min(R,r);//求交集
	}
	if(L>R) return false;//若无交集
	return true;//有交集
}

signed main(){
	cin>>n;
	for(int i=1;i<=n;i++) cin>>pos[i]>>v[i];
	double L=0,R=5e4;//定义二分时间的区间//需根据题意自行判断区间
	for(int i=1;i<=100;i++){//二分次数100次
		double m=(L+R)/2;
		if(check(m)) R=m;
		else L=m;
	}
	cout<<fixed<<setprecision(10)<<L;//输入L,R都可以
}

 二分+哈希求字符串最长回文子串的长度,我们秉着最优的方法,这里就不讲了。

二分+哈希:O(nlogn)    manacher:O(n) 

题目链接:http://oj.daimayuan.top/course/22/problem/698

二分求Lis(最长上升子序)也不讲了。秉着最优原则

题目链接: http://oj.daimayuan.top/course/22/problem/647

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值