二分查找练习

二分查找的前提是该数组是有序的

1.二分查找求下标

这里写图片描述

int binary(int a[], int size, int p)
{
    int low = 0;
    int high = size - 1;//左右端点
    int mid;
    while (low <= high)
    {
        mid = low + (high - low) / 2;
        if (p == a[mid])
            return mid;
        else if (p > a[mid])
            low = mid + 1;
        else
            high = mid - 1;
    }
    return -1;
}

2.二分查找求最大最小值
这里写图片描述

int low(int a[],int size, int p)
{
    int low = 0, high = size-1;
    int mid;
    while (low<=high)
    {
        mid = low + (high - low) / 2;
        if (a[mid] < p)//如果a[mid]<p时说明此时a[mid]小于p成立,但不一定是最优解,可能还有大于mid但比p小的数
            low = mid + 1;
        else
            high = mid - 1;
    }
    return low - 1;// 因为找到mid之后还会测试mid之后一个数,所以返回的数应该是low - 1;
}

3.二分查找的应用

疯牛

时间限制:1000 ms | 内存限制:65535 KB
难度:4
描述
农夫 John 建造了一座很长的畜栏,它包括N (2 <= N <= 100,000)个隔间,这些小隔间依次编号为x1,…,xN (0 <= xi <= 1,000,000,000).
但是,John的C (2 <= C <= N)头牛们并不喜欢这种布局,而且几头牛放在一个隔间里,他们就要发生争斗。为了不让牛互相伤害。John决定自己给牛分配隔间,使任意两头牛之间的最小距离尽可能的大,那么,这个最大的最小距离是什么呢?
输入
有多组测试数据,以EOF结束。
第一行:空格分隔的两个整数N和C
第二行——第N+1行:分别指出了xi的位置
输出
每组测试数据输出一个整数,满足题意的最大的最小值,注意换行。
样例输入
5 3
1
2
8
4
9
样例输出
3
题意:
总共有n个隔间,其中有c头牛有单独占据一个牛圈。问这c头牛间的最大的最小距离。
提示:要求的是最大的最小距离,可以先找出可能距离的范围

#include<iostream>
#include<algorithm>
using namespace std;
int n, c, a[100005];
bool judge(int mid)
{
    int num = 1;//假设已经放了一头牛
    int pre = a[0];//pre是前一个牛舍的编号,根据贪心思想第一间牛舍应该放一头牛
    for (int i = 1; i < n; i++)  //依据牛舍进行判断
    {
        if (a[i] - pre >= mid)//如果a[i]-前一个牛舍编号大于等于mid,则在a[mid]牛舍放入一头牛,num++
        {
            num++;
            pre = a[i];//将前一个放牛的牛舍编号改为a[i];
            if (num >= c)return true;//如果可以放入的牛数量大于等于总牛数量,那么证明此mid可行,返回true
        }
    }
    return false;
}

/*        暂时不知道为什么不对
bool judge(int mid)
{
    int pre = a[0];
    for (int i = 1; i < c; i++)
    {

        int j = 1;//牛舍
        while (a[j] - pre<mid)
        {
            j++; // 1 2 4 8 9
            if (j>n - 1)  return false;
        }
        pre = a[j];
    }

*/
int main()
{
    while (~scanf("%d %d", &n, &c))
    {
        for (int i = 0; i < n; i++)
            cin >> a[i];
        sort(a, a + n);

        int low = 0, high = a[n - 1] - a[0];//确定牛舍之间可能的范围
        int mid;
        while (low <= high)
        {
            mid = low + (high - low) / 2;
            if (judge(mid))   
                low = mid + 1;
            else
                high = mid - 1;

        }
        cout << low - 1 << endl;
    }

    return 0;
}

01:派
描述
我的生日要到了!根据习俗,我需要将一些派分给大家。我有N个不同口味、不同大小的派。有F个朋友会来参加我的派对,每个人会拿到一块派(必须一个派的一块,不能由几个派的小块拼成;可以是一整个派)。

我的朋友们都特别小气,如果有人拿到更大的一块,就会开始抱怨。因此所有人拿到的派是同样大小的(但不需要是同样形状的),虽然这样有些派会被浪费,但总比搞砸整个派对好。当然,我也要给自己留一块,而这一块也要和其他人的同样大小。

请问我们每个人拿到的派最大是多少?每个派都是一个高为1,半径不等的圆柱体。

输入
第一行包含两个正整数N和F,1 ≤ N, F ≤ 10 000,表示派的数量和朋友的数量。
第二行包含N个1到10000之间的整数,表示每个派的半径。
输出
输出每个人能得到的最大的派的体积,精确到小数点后三位。
样例输入
3 3
4 3 3
样例输出
25.133

#include<iostream>
#include<algorithm>
#include<math.h>
using namespace std;
double pi = 3.14159265358979323846;
int n, f;
double s[10005];//存储每个派的体积
bool judge(double mid)
{
    int num = 0;//记录所能分得所有的派的数量,初始化为零
    for (int i = 0; i<n; i++)//记录在体积为mid时,每个派所能分得数量
    {
        num += int(s[i] / (mid * 1000000));
    }
    if (num >= f + 1) return true;//如果当体积为mid时所分的数量大于等于总人数(f+1)时就是这种方案可以保证每人都有,返回true
    else return false;
}
int main()
{
    scanf("%d %d", &n, &f);
    for (int i = 0; i<n; i++)
    {
        double r;
        cin >> r;
        s[i] = pi*r*r * 1000000;//乘1000000是为了使小数点后移,保证精确度
    }
    sort(s, s + n);

    ////二分查找,是要求其最小的最大体积,所以要确定提及可能的变化范围
    double low = 0, high = s[n - 1], mid;//变化范围为0--s【n-1】
    while (high>=low)
    {
        mid = low + (high - low) / 2;

        if (judge(mid))  //如果当mid符合时则继续对low后移,因为此时的mid符合但不一定是最优解
            low = mid + 0.00001;
        else high = mid - 0.00001;
    }
    printf("%.3lf", mid);
    return 0;
}

02:河中跳房子

描述
每年奶牛们都要举办各种特殊版本的跳房子比赛,包括在河里从一个岩石跳到另一个岩石。这项激动人心的活动在一条长长的笔直河道中进行,在起点和离起点L远 (1 ≤ L≤ 1,000,000,000) 的终点处均有一个岩石。在起点和终点之间,有N (0 ≤ N ≤ 50,000) 个岩石,每个岩石与起点的距离分别为Di (0 < Di < L)。

在比赛过程中,奶牛轮流从起点出发,尝试到达终点,每一步只能从一个岩石跳到另一个岩石。当然,实力不济的奶牛是没有办法完成目标的。

农夫约翰为他的奶牛们感到自豪并且年年都观看了这项比赛。但随着时间的推移,看着其他农夫的胆小奶牛们在相距很近的岩石之间缓慢前行,他感到非常厌烦。他计划移走一些岩石,使得从起点到终点的过程中,最短的跳跃距离最长。他可以移走除起点和终点外的至多M (0 ≤ M ≤ N) 个岩石。

请帮助约翰确定移走这些岩石后,最长可能的最短跳跃距离是多少?

输入
第一行包含三个整数L, N, M,相邻两个整数之间用单个空格隔开。
接下来N行,每行一个整数,表示每个岩石与起点的距离。岩石按与起点距离从近到远给出,且不会有两个岩石出现在同一个位置。
输出
一个整数,最长可能的最短跳跃距离。
样例输入
25 5 2
2
11
14
17
21
样例输出
4
提示
在移除位于2和14的两个岩石之后,最短跳跃距离为4(从17到21或从21到25)。

#include<iostream>
#include<algorithm>
using namespace std;
int L, n, m;
int a[50002];
int judge(int mid)
{
    int num = 0;//记录所去除的石头数量
    int pre = 0;//pre表示前一石头距离岸边的距离
    for (int i = 1; i <= n; i++)
    {
        if (a[i] - pre < mid)
        {
            num++;
            if (num>m) return 0;
        }
        else
        {
            pre = a[i];
        }

    }
    return 1;
}
int main()
{
    scanf("%d %d %d", &L, &n, &m);
    for (int i = 1; i <= n; i++)
        cin >> a[i];
    a[++n] = L;//还要把终点距离起点的距离
    sort(a + 1, a + n + 1);
    int low = 0, high = L;//确定石头之间距离可能的范围
    int mid;
    while (low <= high)
    {
        mid = low + (high - low) / 2;
        if (judge(mid))
            low = mid + 1;
        else
            high = mid - 1;
    }
    cout << low - 1 << endl;
}

总结:
个人认为,二分法求解最大的最小值时,二分的是题目需要求的范围,例如派的体积、石头之间的距离、牛舍之间的距离……

if (a[mid] < p)//如果a[mid]<p时说明此时a[mid]小于p成立,但不一定是最优解,可能还有大于mid但比p小的数
            low = mid + 1;
        else
            high = mid - 1;

上方代码low=mid+1,是要尝试下一个值是不是最优解,因为if(a[mid]<屁)成立,只能证明mid可以满足条件,但并不代表就是最大的最小值,所以要继续向后逼近(因为数组a是经过排序的);

发布了24 篇原创文章 · 获赞 5 · 访问量 3958

猜你喜欢

转载自blog.csdn.net/qq_41345281/article/details/81485335