【二分查找学习】

知识准备

结合《算法导论》和《编程珠玑》,下面说明循环不变式的概念与性质。

循环不变式主要用来帮助理解算法的正确性。形式上很类似与数学归纳法,它是一个需要保证正确断言。对于循环不变式,必须证明它的三个性质:

初始化:它在循环的第一轮迭代开始之前,应该是正确的。

保持:如果在循环的某一次迭代开始之前它是正确的,那么,在下一次迭代开始之前,它也应该保持正确。

终止:循环能够终止,并且可以得到期望的结果。

适用范围

从有序数组中查找某个具体的值

假定一个解并判断是否可行

最大化最小值

最大化平均值

例题

给定长度为n的单调不下降数列A0,A1......An-1和一个数k,求满足Ai>=k条件的最小的i。不存在的情况下输出n。

限制条件

1<=n<=10^6

0<=A0<=A1....<=an-1<10^9

0<=k<=10^9

 

首先看到这个题目的数据范围,再根据题目的需求——查找,就应该想到二分查找。

如果朴素地使用顺序查找的话,很容易就会超时。

我们假定输入 n=5 a=2,3,3,5,6 k=3

我们先看第n/2的值,如果a[n/2]>=k的话,就可以知道解不大于n/2.

反之如果a[n/2]<k的话,我们就可以知道解大于n/2。

通过这样一次简单的比较,数据的搜索范围缩小了一半。

之后不断重复这样的过程,最终在O(log n)次的比较之内求得最终的解.

代码实现我就搬运白书上面的了=。=

void solve(){
    //初始化解的范围
    int lb = -1, ub = n;
    
    //重复循环,直到解的存在范围不大于1
    while(ub - lb > 1){
        int mid = (lb+ub)/2;
        if( a[mid] >= k ){
            //如果mid满足条件,则解的存在范围变为了(lb,mid]  左开右闭
            ub = mid; 
        }else{
            //如果不满足条件,则解的范围变为了(mid,ub] 
            lb = mid;
        }
    } 
    
    printf("%d\n", ub);
}
从有序数组中查找某个值

 

接下来再看看写题时最常遇到的一个问题————最大化最小值(最小化最大值)

例题来源:POJ 2456

题目我手写下:农夫约翰搭了一间有N间牛舍的小屋。牛舍排在一条线上,第i号牛舍在xi的位置。但是他的M头牛对小屋都很不满意,因此经常互相攻击。约翰为了防止牛之间的互相伤害,因此决定把每头牛都放在离其他牛尽可能远的牛舍。也就是要最大化最近两头牛直接的距离。 (2<=2<=100000    2<=M<=N  0<=Xi<=10^9)

一开始在最大化最小值这里不太明白,一直在想为什么要让最小值最大。看到后面才明白,哦原来最小值是一个下界,要让这个下界最大而已。

白书上面给的定义是这样的:

C(d)=可以安排牛的位置使得最近任意的牛的距离不小于d

我们推而广之,就是求一个值,这个值使得任意牛之间的间距不小于d。

这个问题使用贪心法,非常容易地去求解。

1.对牛舍的位置x进行排序

2.把第一头牛放入x0的牛舍

3.如果第i头牛放入Xj的话,第i+1头牛就要放入满足Xj+d<= Xk中

代码实现

#include<stdio.h>
#include<algorithm>
using namespace std;
const int N = 100005;
int a[N], n, c;

bool judge(int x)
{
    int cnt = 1, tmp = a[0];
    for(int i = 1; i < n; i++)
    {
        if(a[i] - tmp >= x)
        {
            cnt++;
            tmp = a[i];
            if(cnt >= c) //可以放下C头牛
                return true;
        }
    }
    return false;
}

int get_ans() //二分搜索最小值
{
    int l = 0, r = a[n-1] - a[0];
    while(l <= r)
    {
        int mid = (l + r) / 2;
        if(judge(mid))
            l = mid + 1;
        else
            r = mid - 1;
    }
    return l - 1;
}

int main()
{
    while(~scanf("%d%d",&n,&c))
    {
        for(int i = 0; i < n; i++)
            scanf("%d",&a[i]);
        sort(a, a+n);
        printf("%d\n",get_ans());
    }
    return 0;
}
疯牛

 

转载于:https://www.cnblogs.com/OIerLYF/p/6061371.html

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值