zoukankan      html  css  js  c++  java
  • 7.24 二分搜索

    二分搜索作用:降低时间复杂度到log(n);求满足条件的最大的最小值,或是最小最大值;

    设计一个bool  judge 函数,判断该点是否合法(满足条件)

    A题:

    还记得我们新生赛上的这题Averyboy的筷子这题吗?众所周知,Averyboy是一个非常的男孩,既然是一个非常的男孩,那么他就会有许多奇葩的爱好。比如收藏筷子。现在美美旸有n双筷子,编号为1-n,同一双筷子编号相同,美美旸把这些筷子放成一排。现在美美旸对av-boy说,如果你能解决我给你出的一个问题,这些筷子都归你。美美旸的问题是:把这些筷子通过一些操作,使得最后这些筷子是同一双的一定相连。每次的操作是,你可以从这2n支筷子中任意取出一支,剩下的筷子自动合并在一起,然后把这支筷子插入任意一个位置,包括两端。(操作次数无限制。)为了刁难av-boy,美美旸故意定了一个条件,每次只能取出编号大于等于x的筷子。现在你的问题是找出最大的x(1 <= x <= n),使得av-boy一定能顺利拿到筷子。

     这道题的意思就是说选择一个编号为x的筷子,编号大于等于x的筷子都可以随意移动,那就相当于把编号大于等于x的筷子都删掉,看剩下的筷子编号相同的是不是都连在一起就行,因为剩下的筷子是移动不了的,只能自动合并。由此我们设计出bool judge函数
    接下来二分搜索最大的x即可,注意筷子是2*n个
     1 #include <bits/stdc++.h>
     2 using namespace std;
     3 const int maxn=1e5+10;
     4 int a[maxn];
     5 int b[maxn];
     6 int k,n;
     7 bool judge(int k)
     8 {
     9     int i;
    10     int cnt=1;
    11     for(i=1;i<=2*n;i++)      
    12     {
    13         if(a[i]<k)
    14         {
    15             b[cnt++]=a[i];
    16         }
    17     }
    18     cnt--;
    19     for(i=1;i<=cnt-1;i++)
    20     {
    21         if(i&1)
    22         {
    23             if(b[i+1]!=b[i])return false;
    24         }
    25     }
    26     return true;
    27 }
    28 int bisearch()
    29 {
    30     int l=1,r=n;
    31     int ans=0;
    32     while(l<=r)
    33     {
    34         int mid=(l+r)>>1;
    35         if(judge(mid))
    36         {
    37             ans=mid;
    38             l=mid+1;
    39         }
    40         else r=mid-1;
    41     }
    42     return ans;
    43 }
    44 int main()
    45 {
    46    int t;
    47    cin>>t;
    48    while(t--)
    49    {
    50        scanf("%d",&n);
    51        for(int i=1;i<=2*n;i++)scanf("%d",&a[i]);
    52        int ans=bisearch();
    53        printf("%d
    ",ans);
    54    }
    55     return 0;
    56 }
    View Code

    B题:

    现在有N种物品,每一种物品有一个基础价值a[i],当买k件物品时,第i件物品的最终价值为a[i] + k * i,现在给你S元,你最多能买多少件物品.

     最原始的问题,确定一个k,则每件物品的最终价值都被确定;
    设计bool  judge 函数,对物品的最终价值进行排序,选择最小的k件求和,若和>s,则不合法;
    二分查找最大的k;注意如果i*j的乘积很大,则i,j都要开long long ,不然会爆
    #include <bits/stdc++.h>
    using namespace std;
    const int maxn=1e5+10;
    long long a[maxn];
    long long b[maxn];
    long long n;
    long long s;
    bool judge(long long k)
    {
        int i;
        for(i=1;i<=n;i++)
        {
            b[i]=a[i]+i*k;
        }
        sort(b+1,b+n+1);
        long long sum=0;
        for(i=1;i<=k;i++)
        {
            sum+=b[i];
        }
        if(sum<=s)return true;
        else return false;
    }
    long long bisearch()
    {
        int l=1,r=n;
        long long ans=0;
        while(l<=r)
        {
            long long mid=(l+r)>>1;
            if(judge(mid))
            {
                ans=mid;
                l=mid+1;
            }
            else r=mid-1;
        }
        return ans;
    }
    int main()
    {
    
        int t;
        cin>>t;
        while(t--)
        {
            scanf("%d%lld",&n,&s);
            int i;
            for(i=1;i<=n;i++)scanf("%d",&a[i]);
            long long ans=bisearch();
            printf("%d
    ",ans);
        }
        return 0;
    }
    View Code

    C题:

    众所周知,averyboy是一个非常男孩。天外天给你他一个问题。问题如下:给你一个长度为N的序列a[1]~a[N]和一个整数sum,我们称一个区间为averyboynb区间,当且仅当这个区间的所有数的和不超过sum。现在你需要找出一个区间和最大的averyboynb区间。

    枚举左端点,二分查找右端点;

    对于每一个左端点,二分求解满足条件的最大的右端点

     1 #include <bits/stdc++.h>
     2 using namespace std;
     3 #define ll long long
     4 const int maxn=1e5+10;
     5 int n;
     6 ll sum;
     7 int a[maxn];
     8 ll pre[maxn];
     9 bool judge(int n,int left)
    10 {
    11     if(pre[n]-pre[left-1]<=sum)return true;
    12     else return false;
    13 }
    14 int main()
    15 {
    16     int t;
    17     cin>>t;
    18     while(t--)
    19     {
    20         scanf("%d%lld",&n,&sum);
    21         int i;
    22         pre[0]=0;
    23         for(i=1;i<=n;i++)
    24         {
    25             scanf("%d",&a[i]);
    26             pre[i]=pre[i-1]+a[i]; //前缀和数组 很大程度上减小时间复杂度
    27         }
    28         ll res=0;
    29         for(i=1;i<=n;i++)  //枚举左端点
    30         {
    31             int l=i;
    32             int r=n;
    33             int ans=0;
    34             while(l<=r)     //二分查找右端点
    35             {
    36                 int mid=(l+r)>>1;
    37                 if(judge(mid,i))
    38                 {
    39                     ans=mid;
    40                     l=mid+1;      //找满足条件的最大的右端点
    41                 }
    42                 else r=mid-1;
    43             }
    44             if(ans>0)res=max(res,pre[ans]-pre[i-1]);
    45         }
    46         if(res>0)printf("%lld
    ",res);
    47         else printf("averyboynb
    ");
    48     }
    49     return 0;
    50 }
    View Code

    D题:

    美旸旸现在在腾讯实习。他每工作一天,公司就会给他发一天的工资,但是,当天的工资不一定要当天领,可以等到下次领的时候一起领,但是每次领工资时,之前所有没有领的工资必须这次都领完。现在给你美旸旸工作N天的工资a[1]~a[N],然后给你一个整数M,代表美旸旸一共要领M次工资(不能多也不能少)并且最后一定要把N天的工资领完,毕竟都是美旸旸赚的血汗钱。也就是第N天他是一定要领工资的。(不然就领不完工资了。。。)。现在问题是让你帮美旸旸选择一种领工资的方案,使得这M次领的工资的最大值最小,毕竟美旸旸是一个低调的人,他不想一次领太多钱。你能帮助他吗?

    Sample Input

    1
    7 5
    100
    400
    300
    100
    500
    101
    400

    Sample Output

    500

    此题为典型的二分搜索题,即找最小最大值

    设计bool judge函数,判断当最大值为k时,需要领工资的次数是否<=M,是则合法,否则非法。

    尽可能最多地领工资,一直领到存储的值会大于最大值k

    二分查找最小的最大值k

     1 #include <bits/stdc++.h>
     2 using namespace std;
     3 #define ll long long
     4 const int maxn=1e5+10;
     5 int n,tmax;
     6 int a[maxn];
     7 bool judge(ll x)
     8 {
     9     int i;
    10     ll sum=0;
    11     int t=0;
    12     for(i=1;i<=n;i++)
    13     {
    14         if(sum+a[i]>x)
    15         {
    16             t++;        //在领到a[i]这个工资之前,必须要领一次工资了
    17             sum=a[i];   //领完一次工资之后现在账户存储的值  等价于sum=0;sum+=a[i];
    18         }
    19         else sum+=a[i];  //尽可能的存储最大的工资
    20     }
    21     if(sum)t++;
    22     if(t>tmax)return false;
    23     else return true;
    24 }
    25 int main()
    26 {
    27     int t;
    28     cin>>t;
    29     while(t--)
    30     {
    31         int i;
    32         scanf("%d%d",&n,&tmax);
    33         for(i=1;i<=n;i++)scanf("%d",&a[i]);
    34         int r=0,l=-1;
    35     ll ans=0;
    36     for(i=1;i<=n;i++)
    37     {
    38         r+=a[i];  // 上界是所有的工资之和
    39         l=max(l,a[i]); //下界是单日工资中最大的那一个
    40     }
    41     while(l<=r)
    42     {
    43         int mid=(l+r)>>1;    //算出来的mid不一定是某些工资的和,但是最后一定会减小到那些存在的和。
    44         if(judge(mid))
    45         {
    46             ans=mid;
    47             r=mid-1;       //二分查找满足条件的最小的最大值
    48         }
    49         else l=mid+1;
    50     }
    51     printf("%lld
    ",ans);
    52 
    53     }
    54     return 0;
    55 }
    View Code
  • 相关阅读:
    P1182 数列分段 Section II 题解
    P3853 路标设置题解
    二分模板
    P2678 跳石头题解
    P2440 木材加工题解
    P1024 一元三次方程求解题解
    快速下载vscode的方法
    P1824 进击的奶牛题解
    P1873 砍树题解
    用户登录之asp.net cookie的写入、读取与操作
  • 原文地址:https://www.cnblogs.com/raincle/p/9367921.html
Copyright © 2011-2022 走看看