5.10-5.16周学习总结

  本周学习了二分查找的思想内涵和二分的算法外延。

  首先说说二分查找。二分查找的精髓就是取严格单调递增序列的中位数,然后判断待求数处于左区间或右区间——这样每次取中位数都能去除当前总元素的一半,因此时间复杂度也低至O(logn),是非常优秀的算法了。

  在C++的STL中,二分查找函数有三个:

  binary_search:返回一个bool值判断待求值是否存在给定序列中。

  lower_bound:返回可插入的最小位置的迭代器。可插入的最小位置是啥意思呢?例如给定序列{ 1 2 2 2 3 3 3 4 4 },待求x=3,那么使用该函数会返回4,即第一个符合条件元素的位置

  upper_bound:返回可插入的最大位置的迭代器。这里要注意一点,可插入的最小位置是第一个符合条件元素的位置,那么最大位置是最后一个符合条件的位置吗?不是的,返回的是最后一个符合条件元素的下一个位置,例如给定序列{ 1 2 3 3 3 4 },待求x=3,该函数将返回5;

  函数的用法也很简单——例如lower_bound( a, a+11, 55 ),就是求序列a到a+11位置上,是否存在着55这个数,如果有,返回第一个55的位置,它的返回值是一个迭代器。

  手写二分查找的话,有两点需要注意的地方:

  第一是常规思维下我们会使用 ( left + right ) / 2 即最小值加最大值的和的一半来作为中位数mid使用,但这样有一个缺陷,如果left或者right超过了int类型数值范围上限的一半,那么 (left +right)很可能会溢出,解决办法是使用 ( left + ( right - left ) / 2 ),首先 ( right - left )就是我们所求区间的大小,然后 / 2 得到区间大小的一半,left加上这一半,得到的就是我们的mid了,有效防止了数值溢出。

  第二是手写时难免会碰到{ 1 2 3 3 3 3 4 }这类一堆相同数字的情况,例如在3333中我们很难判断得到的mid是第几个3,如果要求的是第一个3呢?这里就需要注意区间的左右开闭问题,到底是左闭右闭,还是左闭右开,对应着left==right、letf=mid和letf<right、left=mid+1,在手写的时候一定要注意。

  对于无序序列可以用二分法求局部最小值,因为无序序列相当于一个函数图像,在一定的定义域内也可以呈单调趋势。

  问题描述:有一个无序数组,数组中相邻的数一定不相等,找出一个局部最小值。

  思路:1、若arr[0] < arr[1],arr[0]是局部最小值;2、若arr[n] < arr[n-1],arr[n]是局部最小值;3、若arr[n-1] < arr[n] < arr[n+1],arr[n]为局部最小值。

bool M_min(int L, int M, int R)     #确定中间数是否最小
    {
    if(M<L && M<R)
        return True;
    else
        return False;
    }
int BS_AreaMin(int arr)   #在无序数组中,找出一个局部最小值
    {
    if(arr[0]<arr[1])
        return arr[0];
    else if(arr[-1]<arr[-2])
        return arr[-1];
    else
        {
        Left=0;
        Right=len(arr)-1;
        key=False;
        while(not key)
            {
            mid=int(Left+(Right-Left)/2);
            if(M_min(arr[mid-1],arr[mid],arr[mid+1]))   #判断中间数是否最小
                area_min=arr[mid];
                key=True;     #找到一个局部最小值,跳出循环
            else if(arr[mid]>arr[mid-1])
                Right=mid;    #以mid为点,继续二分,在左半区间找
            else if(arr[mid]>arr[mid+1])
                Left=mid;     #以mid为点,继续二分,在右半区间找
            }
        return area_min;    
        }
     }

这是我借鉴python代码改过来的,也不知道对不对……

最后说一下二分法的在算法思维上的使用……这边我还是不怎么懂,只干了两道例题:

Description

It is very hard to wash and especially to dry clothes in winter. But Jane is a very smart girl. She is not afraid of this boring process. Jane has decided to use a radiator to make drying faster. But the radiator is small, so it can hold only one thing at a time.

Jane wants to perform drying in the minimal possible time. She asked you to write a program that will calculate the minimal time for a given set of clothes.

There are n clothes Jane has just washed. Each of them took ai water during washing. Every minute the amount of water contained in each thing decreases by one (of course, only if the thing is not completely dry yet). When amount of water contained becomes zero the cloth becomes dry and is ready to be packed.

Every minute Jane can select one thing to dry on the radiator. The radiator is very hot, so the amount of water in this thing decreases by k this minute (but not less than zero — if the thing contains less than k water, the resulting amount of water will be zero).

The task is to minimize the total time of drying by means of using the radiator effectively. The drying process ends when all the clothes are dry.

Input

The first line contains a single integer n (1 ≤ n ≤ 100 000). The second line contains ai separated by spaces (1 ≤ ai ≤ 109). The third line contains k (1 ≤ k ≤ 109).

Output

Output a single integer — the minimal possible number of minutes required to dry all clothes.

Sample Input

sample input #1
3
2 3 9
5

sample input #2
3
2 3 6
5

Sample Output

sample output #1
3

sample output #2
2

这个问题很像小学数学里面的煎饼(合理安排时间)问题,甚至我第一下看到还有用贪心的冲动……可以想到如果想要晾干衣服的话,最好的方法就是使用机器冲干x秒,然后等待风干y秒,这样就能利用好烘干时不蒸发,不烘干自动蒸发这个特点了。于是我很勉强地二分法,设答案mid,表示能在mid秒钟晒干。
机器烘干需要x秒钟,自然烘干需要mid-x秒钟,得到如下公式x*k+(mid-x) >= a[i]

所以:x >= (a[i] – mid) / (k – 1),需要注意当k==1的时候很容易会出现除0错误。

解题代码如下:


#include <iostream>
#include <algorithm>
#include <cstring>
using namespace std;
const int maxn = 1e5 + 5;
long long a[maxn], k, n;
int check(int x)
{
    long long temp = 0;
    for(int i = 1; i <= n; i++)
    {
        if(a[i] > x)
        {
//            temp += a[i]/k;
//            if(a[i]%k > x-a[i]/k)
//            {
//                temp++;
//            }
//这里把函数写成了贪心!!应该贪心每件衣服最少烘干几次!!
            temp += (a[i]-x)/(k-1);
            if((a[i]-x)%(k-1))   //向上取整
                temp++;
        }
    }
    if(temp > x) return 0;
    else return 1;
}
int main()
{
    while(cin>>n)
    {
        long long maxx = 0;
        for(int i = 1; i <= n; i++)
            {
            cin>>a[i]; 
            maxx = max(maxx, a[i]);
            }
        cin<<k;
        if(k == 1)
        {
            cout<<maxx<<endl;
            continue;
        }
        long long l = 1, r = maxx, mid, ans;
        while(l <= r)
        {
            mid = (l+r)/2;
            if(check(mid))
            {
                ans = mid;
                r = mid - 1;
            }
            else
                l = mid + 1;
        }
        cout<<ans<<endl;
    }
    return 0;
}

——————————————————————————————————————————————————————————————————————————————————

关于二分思想在算法中的应用,我现在还是之后朦朦胧胧一点感觉,但是大概能发现,当出现关键词连续、分段、最值的时候(真的好像贪心啊),可以向二分考虑,特别是使用二分枚举!

希望刷完题之后能有更好一点的思路吧。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值