二分答案

  • 貌似题目让你求啥就要对什么东西进行二分

实数二分

  1. 注意精度,如果要求输出两位小数,建议二分到三、四位

  2. 还是注意精度,P1542不用 long double 都过不去

P1024 [NOIP2001 提高组] 一元三次方程求解

  • 对根所在区间进行二分

  • 因为 ’根与根的差的绝对值 1 ‘,所以枚举 100100 中的每个整数即可,二分的区间的 l,r 分别是 i,i+1

  • 进行二分的条件:f(i)×f(i+1)0

    • 如果 f(i)×f(i+1)=0,且 f(i)=0,那么方程的其中一个根就是 i

    • 如果 f(i+1)=0,暂时不管他,在枚举到 f(i+1),f(i+2) 时再输出,防止重复

  • 关于 check()

    • 由题目得,如果 f(x)×f(y)<0,则在 [x,y] 内存在一个根

    • 所以如果 f(l)×f(mid)<0,就让 r=mid,因为此时根在 [l,mid] 中,反之 l=mid

代码
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int INF=1e9;
const double ACC=1e-7;
int cnt;
double a,b,c,d;
double check(double x){
return 1.0*a*x*x*x+1.0*b*x*x+1.0*c*x+d;
}
signed main(){
// freopen("1.in","r",stdin);
cin>>a>>b>>c>>d;
for(int i=-100;i<100;i++){
double l=i,r=i+1;
double x=check(l),y=check(r);
if(!x){
printf("%.2lf ",double(l));
cnt++;
continue;
}else{
if(x*y>=0) continue;
while(r-l>ACC){
double mid=(l+r)/2;
if(check(mid)*check(l)<=0) r=mid;
else l=mid;
}
printf("%.2lf ",l);
cnt++;
}
if(cnt==3) break;
}
}

P1542 包裹快递

  • 对速度进行二分

  • ‘最大的最小’ 意味着要在当前值符合条件的情况下把右指针左移(在速度更小的区间里寻找符合条件的值)

  • 题目卡精度卡的很严,必须要用 long double

  • 关于 check() :

    • 题目要求最大速度的最小值,由样例解释可得,每段路程的速度是可变的,只要全程的最大速度最小即可

    • 二分到的一个速度不符合条件,意味着至少有一段路程,用最大速度(二分到的速度)跑,仍然赶不上截止期限 y[i]

代码
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int INF=1e9;
const int N=2e5+10;
int n;
int x[N],y[N],s[N],sum;
long double l,r;
bool check(long double max_sp){
long double ti=0;
for(int i=1;i<=n;i++){
if(ti+1.0*s[i]/max_sp>y[i]) return 0;
else ti+=1.0*s[i]/max_sp;
if(ti<x[i]) ti=x[i];
}
return 1;
}
signed main(){
// freopen("1.in","r",stdin);
cin>>n;
for(int i=1;i<=n;i++){
cin>>x[i]>>y[i]>>s[i];
sum+=s[i];
}
l=0; r=1e9;
while(r-l>0.0001){
long double mid=(l+r)/2;
if(check(mid)) r=mid;
else l=mid;
}
printf("%.2Lf\n",l);
}

P1577 切绳子

  • 对切割成的绳子的长度进行二分

  • 输入的绳长都是两位小数,干脆把他们都 ×100,把这道题转化成整数二分来做

  • 关于 check()

    • 对于二分出来的一个长度,枚举每一根绳子,把每根绳子能割出来的绳子数相加

    • 如果加和 sum<k,说明当前长度太长,令 r=mid

    • 反之令 l=mid+1

代码
#include <bits/stdc++.h>
using namespace std;
int n,k;
double in;
int len[10010];
int judge(int length){
int cnt=0;
for(int i=1;i<=n;i++) cnt+=len[i]/length;
return cnt>=k;
}
int main(){
// freopen("1.in","r",stdin);
cin>>n>>k;
for(int i=1;i<=n;i++){
cin>>in;
len[i]=in*100;
}
int l=0;
int r=1e9;
int res=0;
while(l<r){
int mid=l+(r-l)/2;
if(mid==0) break;
if(judge(mid)){
l=mid+1;
res=mid;
}
else r=mid;
}
printf("%.2lf",res*1.0/100);
}

整数二分

P1824 进击的奶牛

  • 对两头牛间的最近距离进行二分

  • 关于 check()

    • 贪心,用 p 记录上一头牛的位置,一旦这个牛棚距离 p 的距离 二分到的最小距离,就 ++cnt ,并且把 p 移动到该牛棚

    • 采用贪心能保证牛棚在该 ‘最小距离’ 下尽可能多的放牛

代码
#include <bits/stdc++.h>
using namespace std;
int pos[100010];
int n,c;
bool judge(int len){
int cnt=1;
int p=1;
for(int i=2;i<=n;i++){
if(pos[i]-pos[p]>=len){
cnt++;
p=i;
}
}
return cnt>=c;
}
int main(){
// freopen("1.in","r",stdin);
cin>>n>>c;
for(int i=1;i<=n;i++) cin>>pos[i];
sort(pos+1,pos+1+n);
int l=0;
int r=pos[n];
int res=r;
while(l<r){
int mid=l+(r-l)/2;
if(judge(mid)){
res=mid;
l=mid+1;
}
else r=mid;
}
cout<<res;
}

P1182 数列分段 Section II

  • 对每段的和的最大值进行二分

  • 因为求的是 ’最大值最小‘ ,所以当前值符合条件的情况下:

    • 如果根据当前最大值分的快多于 m ,就将 l 右移,增大最大值,使数列能被分为更少的块

    • 反之将 r 左移,使分的块更多或在块数符合条件的情况下使最大值最小

代码
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int INF=1e9;
int n,m;
int a[100001];
int l,r;
bool check(int maxi){
int sum=0,cnt=0;
for(int i=1;i<=n;i++){
if(a[i]>maxi) return 0;
if(sum+a[i]>maxi){
sum=a[i];
cnt++;
}else
sum+=a[i];
}
return cnt>=m;
}
signed main(){
// freopen("1.in","r",stdin);
cin>>n>>m;
for(int i=1;i<=n;i++){
cin>>a[i];
r+=a[i];
l=max(l,a[i]);
}
while(l<r){
int mid=(l+r)/2;
if(check(mid)) l=mid+1;
else r=mid;
// cout<<l<<" "<<r<<"\n";
}
cout<<l<<"\n";
}

P2440 木材加工

  • 对分成的木头的长度进行二分

  • 因为有可能根本分不出 k 段来,所以 l 初始为 0,相应的,while(l < r) 改为 while(l+1 < r)

  • check() 部分与 P1577 切绳子 差不多,翻到上面看就行

代码
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int INF=1e9;
int n,k;
int len[100001];
int check(int x){
int cnt=0;
for(int i=1;i<=n;i++)
cnt+=(len[i]/x);
return cnt;
}
signed main(){
// freopen("1.in","r",stdin);
cin>>n>>k;
for(int i=1;i<=n;i++)
cin>>len[i];
int l=0,r=1e18;
while(l+1<r){
int mid=(l+r)/2;
if(check(mid)>=k) l=mid;
else r=mid;
}
cout<<l<<"\n";
}

P1873 [COCI 2011/2012 #5] EKO / 砍树

  • 对锯子的最大高度进行二分
代码
#include <bits/stdc++.h>
using namespace std;
long long hei[1000010];
long long n,m;
bool judge(long long len){
long long cnt=0;
long long p=0;
for(long long i=1;i<=n;i++){
if(hei[i]>len) cnt+=hei[i]-len;
}
return cnt>=m;
}
int main(){
// freopen("1.in","r",stdin);
cin>>n>>m;
for(long long i=1;i<=n;i++) cin>>hei[i];
sort(hei+1,hei+1+n);
long long l=0;
long long r=hei[n];
while(l+1<r){
long long mid=l+(r-l)/2;
if(judge(mid)) l=mid;
else r=mid;
}
cout<<res;
}

P1083 [NOIP2012 提高组] 借教室

  • 对有多少订单符合要求进行二分

  • 关于 check():判断是否有订单不满足要求时用差分进行处理

    bool check(int x){
    memset(temp,0,sizeof(temp));
    for(int i=1;i<=x;i++){
    temp[s[i]]+=d[i];
    temp[t[i]+1]-=d[i];
    }
    for(int i=1;i<=n;i++){
    temp[i]+=temp[i-1];
    if(temp[i]>r[i])
    return 0;
    }
    return 1;
    }
代码
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int INF=1e9;
const int N=1e6+10;
int n,m;
int r[N];
int d[N],s[N],t[N];
int temp[N];
bool check(int x){
memset(temp,0,sizeof(temp));
for(int i=1;i<=x;i++){
temp[s[i]]+=d[i];
temp[t[i]+1]-=d[i];
}
for(int i=1;i<=n;i++){
temp[i]+=temp[i-1];
if(temp[i]>r[i])
return 0;
}
return 1;
}
signed main(){
// freopen("1.in","r",stdin);
cin>>n>>m;
for(int i=1;i<=n;i++)
cin>>r[i];
for(int i=1;i<=m;i++)
cin>>d[i]>>s[i]>>t[i];
if(check(m)){
cout<<"0\n";
return 0;
}
int l=1,r=m;
while(l<r){
int mid=(l+r)/2;
if(!check(mid)) r=mid;
else l=mid+1;
}
cout<<"-1\n"<<r<<"\n";
}

posted on   Bubble_e  阅读(8)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
· 25岁的心里话



点击右上角即可分享
微信分享提示