不务正业-二分基础

来源:互联网 发布:贝尔.格里尔斯 知乎 编辑:程序博客网 时间:2024/05/17 06:20

二分作为查找利器很实用:

本篇主要讲述二分理论

二分解题

二分STL解题

二分模板

非递推版本

int search(int x)  //二分查找{    int left=1,right=n;    while(left<=right)   //注意这里必须等于    {        int mid=(left+right)/2;        if(x>num[mid])            left=mid+1;        else if(x==num[mid])            {                return mid;            }        else            right=mid-1;    }}
题目:

01:查找最接近的元素

//二分查找模板#include <iostream>#include <cstdio>#include <algorithm>#include <cmath>using namespace std;const int maxn = 100001;long long int num[maxn];int n;int search(int key)  //二分查找{    int left=1,right=n;    int ans;    while(left<=right-2)   //注意这里必须等于    {        int mid=(left+right)/2;        if(key>num[mid])            left=mid;        else            right=mid;    }    if(fabs(num[left]-key)<=fabs(num[left+1]-key)&&left<n)        ans = num[left];    else {        ans = num[left+1];    }    return ans;}int main(){    //int n;    long long int m;    long long int tar;    scanf("%d",&n);    for(int i=1;i<=n;i++)    {        scanf("%lld",&num[i]);    }    scanf("%lld",&m);    while(m--)    {        scanf("%lld",&tar);        cout<<search(tar)<<endl;;    }    return 0;}

07:和为给定数


#include <stdio.h>#include <stdlib.h>#include <algorithm>using namespace std;int n;int a[100002];int m;int search(int x){    int l = x+1;    int r = n;    while(l<=r)    {        int mid = (l+r)/2;        if(a[mid]==m-a[x])        return mid;        else if(a[mid]>m-a[x]) r = mid-1;        else l = mid+1;    }    return -1;}int main(){    int tmp;    int i,l;    int r,mid;    scanf("%d",&n);    for(i=1;i<=n;i++)    {        scanf("%d",&a[i]);    }    sort(a+1,a+n+1);//选出小的更小    scanf("%d",&m);    for(i=1;i<=n;i++)    {        tmp = search(i);        if(a[tmp]==m-a[i])//输出结果        {            printf("%d %d\n",a[i],m-a[i]);            return 0;        }    }    printf("No\n");//没有    return 0;}

06:月度开销

题目是要求得开销最小的月度划分的最大开销,不难看出在极端情况(如 每天开销为1,1,50,划分为2个fajo月)时开销最小值是每天开销的最大值(50)。因此,我们可以在读入时找到每天开销的最大值,作为二分查找中的左边界(left)。 
但是右边界(right)在本题目中并不好找,我们只能尽量将它设大一些,此时列举极端情况(如 每天开销为4,5,6,划分为1个fajo月),则发现最小开销为每天开销的总和。于是我们可以把每天开销的和(sum)作为右边界使用。 
接下来是二分查找,此时的mid是作为假定的最小划分的最大开销使用。然后我们需要判断mid是否成立。 
判断的方法如下: 
尝试在每一天的开销中划分,若该fajo月的开销总额没有超过mid,就继续累加;否则将划分出的fajo月总数加1,并重新累加下一个fajo月。在中途若发现该fajo月的开销已经比mid大,就说明mid是错误(false)的。划分完毕后,比较划分出的fajo月总数和m(要求划分出的fajo月总数),若比m大(严格”>”),说明mid不正确;反之正确。 

最后需要改变左右边界值。根据之前判断的结果,若正确,根据题意,要找到最小的划分,所以向下查找(改变right的值);反之,说明mid太小,就向上查找(改变left的值)。

//月份中最大的花费作为左端点,总花费作为右端点进行二分#include <iostream>#include <cstdio>#include <algorithm>using namespace std;const int maxn = 100009;int n,m,maxx,right1,ans;int a[maxn];int check(int x){    int sum = 0;    int yfen = 1;    for(int i=1;i<=n;i++)    {        if(sum+a[i]<=x)        {            sum+=a[i];        }        else        {            sum = a[i];            yfen++;            if(sum>x) return 0;        }    }    if(yfen<=m)    return 1;    else    return 0;}int search(){    int l = maxx;    int r = right1;    int mid;    while(l<=r)    {        mid = (l+r)/2;        if(check(mid))        {            ans = mid;            r = mid-1;        }        else l = mid+1;    }    return ans;}int main(){    cin>>n>>m;    right1 = 0;    for(int i=1;i<=n;i++)    {        cin>>a[i];        right1+=a[i];        if(a[i]>maxx) maxx = a[i];    }    cout<<search()<<endl;    return 0;}

10:河中跳房子

左端点为0,右端点为总长度,二分,

根据i,j之间的距离来决定拆除的石板数,依次枚举

#include <iostream>#include <cstdio>#include <algorithm>using namespace std;const int maxn = 50020;int river[maxn];int L,l,r,n,m;int mid;int ans;int search(int mid){    int cnt = 0,wz = 0;    for(int i=1;i<=n;i++)    {        if(river[i]-river[wz]<=mid)        {            cnt++;        }        else wz = i;    }    return cnt;}int main(){    scanf("%d%d%d",&L,&n,&m);    for(int i=1;i<=n;i++)    {        scanf("%d",&river[i]);    }    n++;    river[n] = L;    l = 0;    r = L;    while(l<=r)    {        int mid = (l+r)/2;        ans = search(mid);        //cout<<ans<<endl;        if(ans>m) r = mid-1;        else l = mid+1;    }    cout<<l<<endl;    return 0;}
 

二分查找模板 nyoj626

#include <iostream>#include <algorithm>#include <cstdio>using namespace std;const int maxn = 50001;int a[maxn],b[maxn];int n,m;int search(int key,int* vec,int len){    int l = 0;    int r = len-1;     while(l<=r)   //注意这里必须等于    {        int mid=(l+r)/2;        if(key>vec[mid])            l=mid+1;        else if(key==vec[mid])            {                return 1;            }        else            r=mid-1;    }    return 0;}int main(){    int m,n,count;    while(~scanf("%d%d",&m,&n))    {        count=0;        if(n==0&&m==0) break;        for(int i=0;i<m;i++)            scanf("%d",&a[i]);        for(int i=0;i<n;i++)            scanf("%d",&b[i]);        if(m>=n)        {            sort(a,a+m);            for(int i=0;i<n;i++)            {                if(search(b[i],a,m))                    count++;            }            printf("%d\n",count);        }        else        {            sort(b,b+n);            for(int i=0;i<m;i++)            {                if(search(a[i],b,n))                    count++;            }            printf("%d\n",count);        }    }    return 0;}






原创粉丝点击