2020年1月5日 林大OJ习题 二分查找

2020年1月5日 二分查找

林大OJ 956 二分查找

二分查找模板题之一,写出来就是手写二分的模板。

#include <bits/stdc++.h>

using namespace std;

int n,x,i,l,r,m,a[1000010];

int seek(int l,int r,int cmp)
{
    while(l<=r)
    {
        m=l+(r-l)/2;
        if(a[m]>cmp) r=m-1;
        if(a[m]==cmp) return m+1;
        if(a[m]<cmp) l=m+1;
    }
    return r+1;
}
int main()
{
    ios::sync_with_stdio(false);
    while(cin>>n>>x)
    {
        for(i=0;i<n;i++)
            cin>>a[i];
        sort(a,a+n);
        printf("%d\n",seek(0,n-1,x));
    }
    return 0;
}
同样也可以直接使用C++中的内置函数:upper_bound。直接用这个函数比自己写的二分速度还快一些,upper_bound作用是返回数组a中第一个大于x的元素的下标ans,用法是ans=upper_bound(a,a+n,x)-a;
#include <bits/stdc++.h>

using namespace std;

const int N=1e6+5;
int a[N];

int main()
{
    int n,x,i;
    while(~scanf("%d%d",&n,&x)){
        for(i=0;i<n;i++)
            scanf("%d",&a[i]);
        int ans=upper_bound(a,a+n,x)-a;
        cout<<ans<<endl;
    }
    return 0;
}

林大OJ 1645 小清新的函数坐标-二分

#include <bits/stdc++.h>

using namespace std;

double y;

double F(double x){
    return 0.0001*x*x*x*x*x+0.003*x*x*x+0.5*x-3;
}

double judge(double y){
    double l=-20.0,r=20.0,m;
    while(r-l>1e-6){
        m=l+(r-l)/2;
        if(F(m)>y) r=m;;//记得写等于号,用ans记下取等于号时对应的m,就不会错
        else l=m;
        if(F(m)==y) return m;
    }
    return m;
}

int main()
{
    ios::sync_with_stdio(false);
    while(cin>>y)
        printf("%.4lf\n",judge(y));
    return 0;
}

林大OJ 1646 小清新的二分查找之旅

同样是二分查找的模板题。

#include <bits/stdc++.h>

using namespace std;

const int N=1e6+1;
int a[N];

bool judge(int a[],int k,int n){
    int mid;
    int l=1,r=n;
    while(l<=r){
        mid=(l+r)/2;
        if(a[mid]>k) r=mid-1;
        if(a[mid]<k) l=mid+1;
        if(a[mid]==k){
            return 1;
            break;
        }
    }
    return 0;
}

int main()
{
    int n,q,i,k;
    while(~scanf("%d%d",&n,&q)){
        for(i=0;i<n;i++)
            scanf("%d",&a[i]);
        for(i=0;i<q;i++){
            scanf("%d",&k);
            if(judge(a,k,n)==1) printf("no\n");
            else printf("YES\n");
        }
    }
    return 0;
}

林大OJ 1647 小清新的二倍问题加强版-二分-桶排

方法1,二分算法,O(n*logn)。来源:大佬的题解

#include <bits/stdc++.h>
using namespace std;
int n,i,l,r,m,cnt,ans,a[10010];
bool seek(int l,int r,int cmp)
{
    while(l<=r)
    {
        m=l+(r-l)/2;
        if(a[m]>cmp)r=m-1;
        if(a[m]<cmp)l=m+1;
        if(a[m]==cmp)return 1;
    }
    return 0;
}
int main()
{
    ios::sync_with_stdio(false);
    cin>>n;
    while(n--)
    {
        ans=cnt=0;
        while(cin>>a[cnt]&&a[cnt])cnt++;
        sort(a,a+cnt);
        for(i=0;i<cnt;i++)
        ans=ans+seek(0,cnt-1,2*a[i]);
        printf("%d\n",ans);
    }
    return 0;
}

方法2,桶排思想(其实就是打标记),O(n)。

#include <bits/stdc++.h>

using namespace std;

const int N=2e5+10,M=1e4+10;
int t,x,n,ans,a[M],b[N];

int main()
{
    ios::sync_with_stdio(false);
    cin>>t;
    while(t--){
        n=0;
        memset(b,0,sizeof(b));
        while(cin>>x &&x)
            b[2*x]=1,a[++n]=x;
        ans=0;
        for(int i=1;i<=n;i++)
            if(b[a[i]]!=0)
                ans++;
        cout<<ans<<endl;
    }
    return 0;
}

林大OJ 1648 小清新切绳子-二分【找最小值的最大值】

二分查找最大值的最小值为ans,judge函数中return的时候大于和等于是同一种情况
等于号在judge(m)中,则满足if(judge(m))时记下ans=m

#include <bits/stdc++.h>

using namespace std;

const long long N=1e7;

int l,r,n,m,k,ans,a[10010];

bool check(int m){
    int s=0;
    for(int i=0;i<n;i++)
        s+=a[i]/m;
    return s>=k;//有等于号
}

int main()
{
    ios::sync_with_stdio(false);
    while(cin>>n>>k){//分成k段
        for(int i=0;i<n;i++)
            cin>>a[i];
        l=0,r=N;
        while(l<=r){
            m=l+(r-l)/2;
            if(check(m))
                ans=m,l=m+1;//满足if(judge(m)),记下ans
            else r=m-1;
        }
        cout<<ans<<endl;
    }
    return 0;
}

林大OJ 1751/洛谷 P1577 切绳子实数版–二分

思路:实数二分,化实数为整数。思路来源:大佬的题解
实数二分,精度会缺失(如果直接用double二分,最后%.2lf输出的时候会自动向上取整,可能改变了答案),解决办法是先把每根绳子长度a[i]乘以100化为整数,再按整数的方法二分,最后输出答案时再除以100即可
注意在二分过程中要特判m=0的情况(否则在judge函数中会除以0导致RE),m=0时直接break退出二分循环,记下答案ans=0。

#include <bits/stdc++.h>

using namespace std;

const long long N=1e4+10;

int l,r,m,a[N],k,n;
double x,ans;

bool check(int m){
    int s=0;
    for(int i=0;i<n;i++)
        s+=a[i]/m;//m!=0才可使用judge函数判断
    return s>=k;//有等于号
}

int main()
{
    ios::sync_with_stdio(false);
    while(cin>>n>>k){//分成k段
        for(int i=0;i<n;i++){
            cin>>x;
            a[i]=(int)(x*100);
        }
        l=0,r=1e7;
        while(l<=r){
            m=l+(r-l)/2.0;
            if(m==0){
                ans=0;break;
            } //n=1 k=10000 a[1]=1.00时,二分过程中会出现m=0的情况,在judge函数中会除以0导致RE
            //要防止RE,则特判二分过程中m=0直接break,退出二分,记下答案ans=0
            if(check(m))
                ans=1.0*m,l=m+1;//满足if(judge(m)),记下ans
            else r=m-1;
        }
        printf("%.2lf\n",ans/100.0);
    }
    return 0;
}

林大OJ 1303 简单几何–二分

这题要特别注意精度:
1、π用acos(-1.0)表示
2、写原始式v2-v1,如果化简v2-v1= h * ( p * r * r - r )= h * r * ( p * r * - 1 ),精度会错
3、实际上还要保留到8位小数而不是4位小数(题目的bug)

#include <bits/stdc++.h>

using namespace std;

const double PI=acos(-1);//定义π

double v1,v2,r,ans;
int t,h;

double vans(int h){
    double l=0,r=100000,m;
    while(r-l>1e-8){
        m=l+(r-l)/2.0;
        v1=m*h;
        v2=PI*m*m*h;
        if(v2-v1<=pow(m,PI))//减小m(减小半径)
        //由于精度要求,if里面必须写原始式v2-v1
            ans=m,r=m;
        else l=m;
    }
    return ans;//题目要求输出保留4位小数,但是这里必须要写到8位小数的精度(题目的bug)
}

int main()
{
    ios::sync_with_stdio(false);
    cin>>t;
    while(t--){
        cin>>h;
        printf("%.4lf\n",vans(h));
    }
    return 0;
}

林大OJ 1211 卖古董–DP–二分【找最大值中的最小值】

二分查找最大值的最小值为ans,judge函数中return的时候小于和等于是同一种情况
等于号在judge(m)中,则满足if(judge(m))时记下ans=m

#include <bits/stdc++.h>

using namespace std;

const int N=1e5+5;
int a[N];
int n,k;

bool check(int x){
    int num=0,tmp=0,s=0;
    for(int i=1;i<=n;i++){
        tmp=0;
        s+=a[i];
        if(s>x){
            s=a[i];
            tmp=1;
            num++;
        }
    }
    if(tmp==0) num++;
    return num>k;
}

int main()
{
    ios::sync_with_stdio(false);
    int t;
    cin>>t;
    while(t--){
        cin>>n>>k;
        int mi=0,s=0,ans=0;
        for(int i=1;i<=n;i++){
            cin>>a[i];
            if(mi<a[i]) mi=a[i];
            s+=a[i];
        }
        int l=mi,r=s,m=0;
        while(l<=r){
            m=l+(r-l)/2;
            if(check(m))
                l=m+1;
            else{
                ans=m;
                r=m-1;
            }
        }
        cout<<ans<<endl;
    }
    return 0;
}

林大OJ 1733/洛谷 P1182 数列分段

其实此题与卖古董题是同一道题,思路基本一致。

#include <bits/stdc++.h>

using namespace std;

const int N=1e5+5;
int a[N];
int n,k;

bool check(int x){
    int num=0,tmp=0,s=0;
    for(int i=1;i<=n;i++){
        tmp=0;
        s+=a[i];
        if(s>x){
            s=a[i];
            tmp=1;
            num++;
        }
    }
    if(tmp==0) num++;
    return num>k;
}

int main()
{
    ios::sync_with_stdio(false);
    cin>>n>>k;
    int mi=0,s=0,ans=0;
    for(int i=1;i<=n;i++){
        cin>>a[i];
        if(mi<a[i]) mi=a[i];
        s+=a[i];
    }
    int l=mi,r=s,m=0;
    while(l<=r){
        m=l+(r-l)/2;
        if(check(m))
            l=m+1;
        else{
            ans=m;
            r=m-1;
        }
    }
    cout<<ans<<endl;
    return 0;
}

林大OJ 1245 二分查找加强版

二分查找模板,来源于ljw学长的本题题解
模板1:在[l,r]区间内查找升序排列的数列a中存在的数cmp的下标(数据保证cmp在数列中,例题:nefu 1236)
int seek(int l,int r,int cmp)
{
    while(l<=r)
    {
        m=l+(r-l)/2;
        if(a[m]>cmp)
            r=m-1;
        if(a[m]==cmp)
            return m;
        if(a[m]<cmp)
            l=m+1;
    }
}
模板2:在[l,r]区间内查找升序排列的数列a中第一个比cmp大的数的下标(cmp可能在数列中也可能不在数列中,例题:nefu 956、nefu 1245)
int seek(int l,int r,int cmp)
{
    while(l<=r)
    {
        m=l+(r-l)/2;
        if(a[m]>cmp)
            r=m-1;
        if(a[m]==cmp)
            return m+1;
        if(a[m]<cmp)
            l=m+1;
    }
    return r+1;
}
本题代码:
#include <bits/stdc++.h>

using namespace std;

int n,x,i,l,r,m,a[2000010];

int check(int l,int r,int cmp)
{
    while(l<=r)
    {
        m=l+(r-l)/2;
        if(a[m]>cmp)
            r=m-1;
        if(a[m]==cmp)
            return m+1;
        if(a[m]<cmp)
            l=m+1;
    }
    return r+1;
}
int main()
{
    ios::sync_with_stdio(false);
    while(cin>>n>>x)
    {
        for(i=0;i<n;i++)
            cin>>a[i];
        sort(a,a+n);
        printf("%d\n",check(0,n-1,x));
    }
    return 0;
}
还可以直接用函数upper_bound!!
#include <bits/stdc++.h>
using namespace std;
int n,x,i,a[2000010];
int main()
{
    ios::sync_with_stdio(false);
    while(cin>>n>>x)
    {
        for(i=0;i<n;i++)
        cin>>a[i];
        sort(a,a+n);
        printf("%d\n",upper_bound(a,a+n,x)-a);
    }
    return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值