大一寒假集训2020.1.5 //二分查找

大一寒假集训2020.1.5

二分法

首先,二分中最基础的二分查找。
一个非常神奇的算法/思想:
二分,分的是答案,直接在答案在的区间范围中二分,分出一个值,就判断是不是答案,并进行转移
如果已知候选答案的范围(min,max)(单调有序),有时候我们不必通过计算得到答案,只需在此范围内应用“二分”的过程,逐渐靠近答案(最后,得到答案)!
通过二分的方法,大幅度地跳过一片没必要的比较和选择

在C++中有两个函数,可以直接找出大于等于要找数下标。
upper_bound()与lower_bound()使用方法
都是二分函数,头文件 “algorithm” 也可以用 “bits/stdc++.h”
upper_bound返回第一个大于的元素的下标;
lower_bound返回第一个大于等于元素的下标;
int point[10] = {1,3,7,7,9};
int tmp = upper_bound(point, point + 5, 7) - point;//按从小到大,7最多能插入数组point的哪个位置
printf("%d\n",tmp);
tmp = lower_bound(point, point + 5, 7) - point;////按从小到大,7最少能插入数组point的哪个位置
printf("%d\n",tmp);
二分查找
直接使用刚提到的函数一行解决。

#include<bits/stdc++.h>
using namespace std;
int main()
{
    int x,n;
    while(cin>>n>>x)
    {
        int a[1000005];
        for(int i=0;i<n;i++)
        {
            cin>>a[i];
        }
        int ans;
        ans=upper_bound(a,a+n,x)-a;
        cout<<ans<<endl;
    }
    return 0;
}

小清新的二分查找之旅

看起来还是不难的,注意看题,找到返回NO,未找到返回YES

#include <bits/stdc++.h>
using namespace std;
int n, q;
int a[1000005];
int erfen(int a[], int left, int right, int x)
{
    int mid;
    while (left < right)
    {
        mid = (left + right) / 2;
        if (a[mid] == x)
        {
            return mid;
        }
        else if (a[mid] < x)
        {
            left = mid + 1;
        }
        else
        {
            right = mid;
        }
    }
    return 0;
}
int main()
{
    while (scanf("%d%d", &n, &q) != EOF)
    {
        int t;
        for (int i = 0; i < n; i++)
        {
            scanf("%d", &a[i]);
        }
        for (int i = 0; i < q; i++)
        {
            scanf("%d", &t);
            if (t < a[0] || t > a[n - 1])
            {
                printf("YES\n");
            }
            else if (erfen(a, 0, n - 1, t))
            {
                printf("no\n");
            }
            else
            {
                printf("YES\n");
            }
        }
    }
}

小清新的函数坐标-二分

坐标是实数,注意输入输出格式,还有二分的条件写法就可以了。

#include <bits/stdc++.h>
using namespace std;
double f(double x)
{
    return 0.0001 * x * x * x * x * x + 0.003 * x * x * x + 0.5 * x - 3;
}
double find1(double l, double r, double y)
{
    double mid = (l + r) / 2;
    while (r - l >= 1e-6)
    {
        if (f(mid) > y)
        {
            r = mid;
        }
        else
        {
            l = mid;
        }
        mid = (l + r) / 2;
    }
    return mid;
}
int main()
{
    double y;
    double p;
    while (scanf("%lf", &y) != EOF)
    {
        p = find1(-20, 20, y);
        printf("%.4f\n", p);
    }
    return 0;
}

小清新的二倍问题加强版-二分-桶排

能用桶排干嘛要用二分呢

#include <bits/stdc++.h>
using namespace std;
int tong[100005];
int main()
{
    int n;
    cin>>n;
    while (n--)
    {
        memset(tong, 0, sizeof(tong));

        int ans = 0;
        int t;
        scanf("%d", &t);
        while (t)
        {
            tong[t]++;
            scanf("%d", &t);
        }
        for (int i = 1; i <= 50000; i++)
        {
            if (tong[i] != 0 && tong[2 * i] != 0)
            {
                ans++;
            }
        }
        cout << ans << endl;
    }
    return 0;
}

简单几何-二分

把两种体积都表示出来,在给定区间内不断二分,找出要求的值。

#include <bits/stdc++.h>
using namespace std;
const double PI = acos(-1.0);
double yz(double r, int h)
{
    return PI * r * r * h;
}
double v1(double r, int h)
{
    return r * h;
}
int h;
double find2(double l, double r, double h)
{
    double mid = (l + r) / 2.0;
    while (r - l > 1e-8)
    {
        if (yz(mid, h) - v1(mid, h) <= pow(mid, PI))
        {
            r = mid;
        }
        else
        {
            l = mid;
        }
        mid = (l + r) / 2.0;
    }
    return mid;
}
double a[1000005];
int main()
{
    int t;
    memset(a, 0, sizeof(a));
    for (int i = 1; i <= 100000; i++)
    {
        a[i] = find2(0, 100000, i);
    }
    scanf("%d", &t);
    while (t--)
    {
        scanf("%d", &h);
        printf("%.4f\n", a[h]);
    }
    return 0;
}

用二分求最大最小值,最小最大值

重头戏来了,这是蒟蒻认为最难解决的一个问题,还好有一些规律可循。
(虽然我看不懂)

小清新切绳子-二分

//二分check()函数总结:一些值都符合条件,也就是求最大值时,check()函数 >=k时为返回1,
//对应的答案也在check() 返回1时记录就行!
//当一些值都符合条件,也就是求最小值时,check()函数>k返回为1,
//对应的答案在=号的位置,也就是check()返回0时记录就行!
#include <bits/stdc++.h>
using namespace std;
int n, k;
int a[10005];
int check(int x)
{
    int num = 0;
    for (int i = 0; i < n; i++)
    {
        num += a[i] / x;
    }
    if (num >= k)
    {
        return 1;
    }
    else
    {
        return 0;
    }
}
int main()
{
    while (scanf("%d %d", &n, &k) != EOF)
    {
        int mx = -99999999;
        for (int i = 0; i < n; i++)
        {
            scanf("%d", &a[i]);
            if (mx < a[i])
            {
                mx = a[i];
            }
        }
        int mid = 0;
        int ans = 0;
        int l, r;
        l = 0;
        r = mx;
        while (l <= r)      //一定要有等于;
        {
            mid = (l + r) / 2;
            if (check(mid) == 1)
            {
                ans = mid;
                l = mid + 1;
            }
            else
            {
                r = mid - 1;
            }
        }
        printf("%d\n", ans);
    }
    return 0;
}

卖古董-DP-二分

上一题最小值最大,这就最大值最小。那我就来了。按照上一题的注释。搞一个check函数就OK

#include <bits/stdc++.h>
using namespace std;
int a[100005];
int n, m;
int check(int mid)
{
    int num = 0;
    int tmp = 0, s = 0;
    for (int i = 1; i <= n; i++)
    {
        tmp = 0;
        s = s + a[i];
        if (s > mid)
        {
            s = a[i];
            tmp = 1;
            num++;
        }
    }
    if (tmp == 0)
        num++;
    if (num > m)
        return 1;
    else
        return 0;
}
int main()
{
    int t;
    cin >> t;
    while (t--)
    {
        scanf("%d%d", &n, &m);
        memset(a, 0, sizeof(a));
        int mx = -999999;
        int sum=0;
        for (int i = 1; i <= n; i++)
        {
            scanf("%d", &a[i]);
            if (mx < a[i])
            {
                mx = a[i];
            }
            sum+=a[i];
        }
        int l, r;
        r = sum;
        l = mx;
        int ans = 0;int mid = 0;
        while (l <= r)
        {
            mid = (l + r) / 2;
            if (check(mid) == 1)
            {
                l = mid + 1;
            }
            else
            {
                ans = mid;
                r = mid - 1;
            }
        }
        printf("%d\n", ans);
    }
    return 0;
}

切绳子实数版-二分

没什么特别需要注意的,就注意一下实数处理的小套路

#include <bits/stdc++.h>
//实数进位问题,先转成整型;
using namespace std;
int a[100005];
int n, k;
double t;
int check(int mid)
{
    long long num = 0;
    for (int i = 1; i <= n; i++)
    {
        num += (int)(a[i] / mid);
    }
    if (num >= k)
    {
        return 1;
    }
    else
    {
        return 0;
    }
}
int main()
{
    while (scanf("%d%d", &n, &k) != EOF)
    {
        double mx = -999999;
        for (int i = 1; i <= n; i++)
        {
            scanf("%lf", &t);
            a[i] = (int)(t * 100);
            if (mx < a[i])
            {
                mx = a[i];
            }
        }
        int mid = 0;
        int l, r, ans = 0;
        r = mx;
        l = 0;
        while (l <= r)
        {
            mid = (l + r) / 2.0;
            if (mid == 0)
                break;
            if (check(mid) == 1)
            {
                ans = mid;
                l = mid + 1;
            }
            else
            {
                r = mid - 1;
            }
        }
        printf("%.2f", ans / 100.00);
    }
    return 0;
}

数列分段-二分

难点依旧是check函数的写法,就按照之前说的方法多练习,毕竟都是熬过来的。

#include <bits/stdc++.h>
using namespace std;
int a[100005];
int m, n;
int check(int mid)
{
    int num = 0;
    int tmp = 0, s = 0;
    for (int i = 1; i <= n; i++)
    {
        tmp = 0;
        s = s + a[i];
        if (s > mid)
        {
            s = a[i];
            tmp = 1;
            num++;
        }
    }
    if (tmp == 0)
        num++;
    if (num > m)
        return 1;
    else
        return 0;
}
int main()
{
    while (scanf("%d%d", &n, &m) != EOF)
    {
        int mx = -9999;
        int sum = 0;
        for (int i = 1; i <= n; i++)
        {
            scanf("%d", &a[i]);
            if (mx < a[i])
            {
                mx = a[i];
            }
            sum += a[i];
        }
        int mid = 0;
        int ans = 0;
        int l = mx, r = sum;
        while (l <= r)
        {
            mid = (l + r) / 2;
            if (check(mid) == 1)
            {
                l = mid + 1;
            }
            else
            {
                ans = mid;
                r = mid - 1;
            }
        }
        printf("%d\n", ans);
    }
}

二分查找加强版

所谓的加强版也就是多个排序,而且sort都能用

#include<bits/stdc++.h>
using namespace std;
int a[2000010];
int main()
{
    int x,n;
    while(cin>>n>>x)
    {
        memset(a,0,sizeof(a));
        for(int i=0;i<n;i++)
        {
            cin>>a[i];
        }
        sort(a,a+n);
        int ans;
        ans=upper_bound(a,a+n,x)-a;
        cout<<ans<<endl;
    }
    return 0;
}

拖好久真的是这几天的题太恶心,好好消化一下在看就有不一样的感悟。

发布了7 篇原创文章 · 获赞 0 · 访问量 1297

猜你喜欢

转载自blog.csdn.net/qq_34212975/article/details/103942926