不务正业-二分基础

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

本篇主要讲述二分理论

二分解题

二分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;
}






评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值