二分答案

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

实数二分

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

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

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

  • 对根所在区间进行二分

  • 因为 ’根与根的差的绝对值 \(\ge 1\) ‘,所以枚举 $-100 \to 100 $ 中的每个整数即可,二分的区间的 \(l,r\) 分别是 \(i,i+1\)

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

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

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

  • 关于 \(check()\)

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

    • 所以如果 \(f(l) \times 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 切绳子

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

  • 输入的绳长都是两位小数,干脆把他们都 \(\times 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\) 的距离 \(\ge\) 二分到的最小距离,就 \(++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 2023-06-21 08:09  Bubble_e  阅读(34)  评论(0)    收藏  举报