二分学习笔记
写在前面:本文中的“单调”不包括“单调不变”。(我不说你们应该也不会想到)
一、算法引入
如果我们要用一个数列(各个位置要有相应的数字形式的下标,且我们的这个下标可为小数)的某一个子序列来求一个值,那么当这个子序列的值是单调的,那么显然为了让我们整个过程的平均复杂度低一些,我们可以考虑使用这样或类似的算法:设
这样的算法大概就是二分算法了。
Tip:二分算法包括二分查找、二分答案、整体二分等算法。
这里我要先说一下,二分法刚学时你可能会感觉它很难,但只要你多模拟二分的过程,肯下功夫,你也会慢慢领悟到其中的奥妙了。(然后你也许就可以秒切各种二分可以做的很水的部分分,甚至直接用二分秒切很多题了(doge))
二、算法应用
2.1 二分查找
指在一个数列中用二分法来查找某个值。
例题一 Luogu P2249查找
#include<bits/stdc++.h>
using namespace std;
int a[1000005],m,q,n;
int find(int x){
int l=1,r=n,mid;
while(l<r){
mid=l+r>>1;//下面五行为基本判断+端点修改
if(a[mid]>=x){
r=mid;
}
else{
l=mid+1;
}
}
if(a[r]==x){
return r;
}
else{
return -1;
}
}
int main(){
cin>>n>>m;
for(int i=1;i<=n;i++){
cin>>a[i];
}
for(int i=1;i<=m;i++){
cin>>q;
cout<<find(q)<<" ";
}
return 0;
}
2.2 二分答案
顾名思义,大概就是对某一个问题的答案进行二分,以求出一个最优解
例题一 Luogu P1873 EKO/砍树
//本题要我们求最大的一个可以得到至少M米木材的H
//我们可以发现,H值变大的过程中砍下木材的长度是单调不增的,那么我们就可以进行相关的二分答案了(以H为键值)
#include<bits/stdc++.h>
using namespace std;
long long a[2000000],m,n;
bool p(int h){
long long ans=0;
for(int i=1;i<=n;i++){
if(a[i]>h){
ans+=(a[i]-h);
}
}
return ans>=m;
}
int main(){
long long l=0,r=1e9,g,mid;
scanf("%lld%lld",&n,&m);
for(int i=1;i<=n;i++){
scanf("%lld",&a[i]);
}//下面的一段while循环为本题二分答案的主体部分
while(l<=r){
mid=l+r>>1;
if(p(mid)){//判断
g=mid;l=mid+1;//g是用来记录答案的
}
else{
r=mid-1;
}
}
printf("%lld",g);
return 0;
}
例题二 Luogu P1542 包裹快递
大体的算法与上一题的差不多,但要注意精度(lmid是用来防止死循环的)
//二分
//要用long double
#include<bits/stdc++.h>
#define F(i,j,k) for(int i=j;i<=k;++i)
#define ldb long double
using namespace std;
ldb l=0.0,r=10000000.0,ans=1.0*INT_MAX,ll=-1,rr=1;
int n;
struct node{
int x,y,s;
}pos[200005];
int read(){
int w=0;
char c=getchar();
while(c<'0'||c>'9')
c=getchar();
while(c>='0'&&c<='9'){
w=(w<<3)+(w<<1)+c-'0';
c=getchar();
}
return w;
}
ldb mmax(ldb aa,ldb bb){
if(aa>=bb)
return aa;
return bb;
}
ldb mmin(ldb aa,ldb bb){
if(aa<=bb)
return aa;
return bb;
}
int main(){
n=read();
F(i,1,n){
pos[i].x=read();pos[i].y=read();pos[i].s=read();
}ldb mid=l+r/2.00;
while(l<=r){//ll=l,rr=r;
bool ok=true;
ldb mid=(l+r)/2.0;
ldb tim=0.00;
for(int i=1;i<=n;++i){
ldb tt=1.00*pos[i].s/mid+1.00*tim;
if(tt>1.00*pos[i].y){
ok=false;
break;
}
else{
tim=mmax(tt,1.00*pos[i].x);
}
}
if(ok&&mid<=lmid){
r=1.0*mid-0.0001;
ans=mmin(ans,mid);
}
else{
l=1.0*mid+0.0001;
}
}
printf("%.2Lf",ans);
return 0;
}
二分查找和二分答案的注意事项:
1."
2.修改
3.一定要反复确认是不是所有情况都考虑到了,二分的代码又是否适用于所有情况。
to be continued;
2.3 整体二分
to be continued;
三、习题
3.1 Luogu P1102 A-B数对
to be continued;
本文如有什么大问题,请私信我或在本篇博客文章下评论来告知我。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 如何调用 DeepSeek 的自然语言处理 API 接口并集成到在线客服系统
· 【译】Visual Studio 中新的强大生产力特性
· 2025年我用 Compose 写了一个 Todo App