[Summary] dichotomy (binary search)

[Summary] dichotomy (binary search)

I, on the dichotomy

Dichotomy is a very common but very important algorithms. Dichotomy can be a great help to our problem-solving.

1. premise

Applicable conditions are sequence dichotomy 二分性, i.e. monotonicity. When the sequence has a dichotomy, then we continue to enumerate the midpoint of the interval to determine whether the value of the title set conditions.
When such occurs in the title 最大值的最小, 最小值的最大when the question, the answer has dichotomies.

2. Classification

From the binary object classification, we can only half the final answer, we can judge half.
Dichotomous classification of the types can be divided into two sub-fields on the integer and half on the real domain .

3. error-prone point

Dichotomy simple easy to write, but very easy wrong. There are many ways to achieve half, of which the details of the place requires careful consideration.
For binary integer on the domain:
We need to pay attention to the termination conditions change interval around the position, to avoid missing answers or cause an infinite loop.
For half of the real number field:
we need to pay attention to control accuracy.
Own proposals form a fixed code model, to avoid unnecessary errors.

4. extending dichotomy

C ++ STL in lower_bound, upper_boundit can also solve implemented in a binary search sequence to an integer ksuccessor.
Monotonous dichotomy can solve the problem, further, we can extend to dichotomy 三分法. At this time, the rule of thirds can solve extreme value problems unimodal function.

Second, on binary integer domain

1. Templates

Here we are given a common template:

while(l<=r){
    int mid=(l+r)>>1;
    if(check(mid)){
        ans=mid;
        r=mid-1;
    }
    else l=mid+1;
}

Second, two points on the real number field

1. Templates

Two points on the real number field is relatively simple as r-lreaching the required accuracy we can.

#define eps 1e-5
while(r-l>eps){
    double mid=(l+r)/2;
    if(check(mid)) r=mid;
    else l=mid;
}

When we are not sure of the accuracy, we can be in the form of a fixed number of cycles calculated using. Accuracy of the results obtained in this way is generally set higher than eps:

for(int i=1;i<=100;i++){
    double mid=(l+r)/2;
    if(check(mid)) r=mid;
    else  l=mid;
}

Third, exercise

Example 1: # 9,100,055 "1.2 cases through a 1" angry cow / SP297 AGGRCOW - Aggressive Cows / P1316 lost caps

Analysis: The
very basic dichotomy, each time interval of half cow, the cow if you can put down this c, then continue to expand this distance, otherwise narrow the distance, until you find the answer.
code show as below:

#include<bits/stdc++.h>
using namespace std;
int f[1000050],n,c,rem;
int judge(int x){
    int num=0;
    int temp=f[1]; 
    for(int i=2;i<=n;i++){
        if(f[i]-temp<x) num++;
        else temp=f[i];
        if(num>rem) return 0;
    }
    return 1;
}
int main()
{
    scanf("%d%d",&n,&c);
    for(int i=1;i<=n;i++) scanf("%d",&f[i]);
    sort(f+1,f+n+1);
    rem=n-c;
    int maxn=0;
    int l=1,r=f[n]-f[1];
    while(l+1<r){
        int mid=(l+r)/2;
        if(judge(mid)) l=mid; 
        else r=mid;
    }
    printf("%d\n",l);
    return 0;
}

Example 2: P1661 diffusion

Analysis:
disjoint-set + half the answer. Enumeration form a bipartite communication time blocks, each time using disjoint-set statistics, the number is greater than 1 if the last set, then move the left section, the right section or mobile. Note that two points will be expanded, so the unit will go twice the distance of time.
code show as below:

#include<bits/stdc++.h>
#define N 100010
using namespace std;
int sx[N],sy[N],pre[N],n;
int Find(int x){
    return (pre[x]==x)? x:Find(pre[x]);
}
int check(int mid){
    for(int i=1;i<=n;i++) pre[i]=i;
    for(int i=1;i<=n;i++){
        for(int j=i+1;j<=n;j++){
            int dis=abs(sx[i]-sx[j])+abs(sy[i]-sy[j]);
            if(mid*2>=dis){
                int fi=Find(i);
                int fj=Find(j);
                if(fi!=fj) pre[fi]=fj;
            }
        }
    }
    int cnt=0;
    for(int i=1;i<=n;i++) if(pre[i]==i) cnt++;
    return (cnt==1)? 1:0;
}
int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
        scanf("%d%d",&sx[i],&sy[i]);
    int l=0,r=1e9,ans=0;
    while(l<=r){
        int mid=(l+r)>>1;
        if(check(mid)){
            r=mid-1;
            ans=mid;
        }
        else l=mid+1;
    }
    printf("%d",ans);
    return 0;
}

Example. 3: P1182 series segments Section II

Analysis:
binary enumeration is feasible when the sum of each segment of mid, sub-section is updated more than m interval left, the right or the update interval.
code show as below:

#include<bits/stdc++.h>
using namespace std;
int a[100010];
int n,m,ans,l,r;
int judge(int mid){
    int sum=0,cnt=1;
    for(int i=1;i<=n;i++){
        if(sum+a[i]<=mid)
            sum+=a[i];
        else{
            sum=a[i];
            cnt++;
        }
    }
    return (cnt<=m)?1:0;
}
int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++){
        scanf("%d",&a[i]);
        l=max(l,a[i]);
        r+=a[i];
    }
    while(l<=r){
        int mid=(l+r)>>1;
        if(judge(mid)){
            ans=mid;
            r=mid-1;
        }
        else l=mid+1;
    }
    printf("%d",ans);
    return 0;
}

例4:POJ 2018 Best Cow Fences

Analysis:
two points on the real domain. Just because the average number and description of the relationship between the number of discrete, so we also have plus or minus no effect on the average for the entire sequence. So we sequences After subtracting the average, the problem exists or not into a sequence that is greater than 0 and interval.
We \ (O (N) \) provided with the prefix and the complexity of processing a full sequence of subtracted maximum period sequence, if the sequence at this time is less than 0, then we enumerate average value is too large, and thus indentations the right range, whereas empathy.

#include<bits/stdc++.h>
#define N 100010
#define INF 1e10
using namespace std;
double a[N],b[N],sum[N];
int main()
{
    int n,len;
    scanf("%d%d",&n,&len);
    for(int i=1;i<=n;i++) scanf("%lf",&a[i]);
    double l=-1e6,r=1e6;
    double dlt=1e-5;
    while(r-l>dlt){
        double mid=(l+r)/2;
        for(int i=1;i<=n;i++) b[i]=a[i]-mid;//削去平均值 
        for(int i=1;i<=n;i++) sum[i]=sum[i-1]+b[i];//求前缀和 
        double ans=-INF,temp=INF;
        for(int i=len;i<=n;i++){
            temp=min(temp,sum[i-len]);//因为长度大于等于L,所以确定一个min左端点 
            ans=max(ans,sum[i]-temp);
        }
        if(ans>=0) l=mid;//可以达到该平均值 
        else r=mid;
    }
    printf("%d",int(r*1000));
    return 0;
}

例5:CF670C Cinema

Analysis:
greedy thought, half enumerate every movie can understand the number of dubbing and subtitles understand the number of people, first of all meet to understand the number of dubbing, secondly to meet the number of read subtitles.

#include<bits/stdc++.h>
#define N 200010
using namespace std;
inline void read(int &x){
    x=0;int flag=1;char ch=getchar();
    while(ch<'0'||ch>'9'){
        if(ch=='-') flag=-1;
        ch=getchar();
    }
    while(ch>='0'&&ch<='9'){
        x=(x<<1)+(x<<3)+ch-'0';
        ch=getchar();
    }
    x*=flag;
}
int n,m,a[N],b[N],v,pow1,pow2,last1,last2;
int ans=1;
int main()
{
    read(n);
    for(int i=1;i<=n;i++) read(a[i]);
    sort(a+1,a+n+1);read(m);
    for(int i=1;i<=m;i++) read(b[i]);
    for(int i=1;i<=m;i++){
        read(v);
        int pow1=(upper_bound(a+1,a+n+1,b[i])-a-1)-(lower_bound(a+1,a+n+1,b[i])-a-1);
        int pow2=(upper_bound(a+1,a+n+1,v)-a-1)-(lower_bound(a+1,a+n+1,v)-a-1);
        if(pow1>last1||(pow1==last1&&pow2>last2)) last1=pow1,last2=pow2,ans=i;
    }
    printf("%d",ans);
    return 0;
}

Example 6: POJ3579 Median

code show as below:

#include<iostream>
#include<cstdlib>
#include<cstdio>
#include<algorithm>
const int N=1e6;
using namespace std;
int a[N],n,m,ans;
int check(int val)
{
    int cnt=0;
    for(int i=1;i<=n;i++)
        cnt+=n-(lower_bound(a+1,a+n+1,a[i]+val)-a-1);
    if(cnt>m) return 1;
    else return 0;
}
int main()
{
    while(~scanf("%d",&n))
    {
        m=n*(n-1)/4;
        ans=-1;
        for(int i=1;i<=n;i++)
            scanf("%d",&a[i]);
        sort(a+1,a+n+1);
        int l=1,r=a[n]-a[1];
        while(l<=r){
            int mid=(l+r)>>1;
            if(check(mid)){
                ans=mid;
                l=mid+1;
            }
            else r=mid-1;
        }
        printf("%d\n",ans);
    }
    return 0;
}

Example 7: P1083 borrow classroom

Analysis:
a good thinking questions, using the idea of difference. We borrowed a k classrooms in the i-th day, at this time node k accumulate these classrooms, at the time of the j-day return of minus. So we can know on any given day lent number of classrooms. We can not continue to meet half of this date, if the final result is m, then the whole can meet. Or the date of termination of the position that they can not meet the half.
code show as below:

#include<bits/stdc++.h>
using namespace std;
int num[1000010],day[1000010];
int m,n,l[1000010],r[1000010],req[1000010];
int judge(int mid){
    memset(day,0,sizeof(day));
    for(int i=1;i<=mid;i++){
        day[l[i]]+=req[i];
        day[r[i]+1]-=req[i];
    }
    if(day[1]>num[1]) return 0;
    for(int i=2;i<=n;i++){
        day[i]+=day[i-1];
        if(day[i]>num[i]) return 0;
    }
    return 1;
}
int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++) scanf("%d",&num[i]);
    for(int i=1;i<=m;i++) scanf("%d%d%d",&req[i],&l[i],&r[i]);
    int L=1,R=m,ans=0;
    while(L<=R){
        int mid=(L+R)>>1;
        if(judge(mid)){
            L=mid+1;
        }
        else R=mid-1,ans=mid;
    }
    if(R!=m) printf("-1\n%d",ans);
    else printf("0");
    return 0;
}

Guess you like

Origin www.cnblogs.com/cyanigence-oi/p/11729941.html