acm大一寒假集训--二分查找/二分算法

题目来源于东北林业大学OJ

东北林业大学OJ,点击进入👈👈👈

好困好想哭!!!
今天的文章来得特别晚,因为,现在才AK
二分太难了!!!
玄学
今天的题我怎么ac的?
我不知道

不多废话 ,上题

题目一:二分查找

Description
有n(1<=n<=1000005)个整数,已经按照从小到大顺序排列好,现在另外给一个整数x,请找出序列中第1个大于x的数的下标!
Input
输入数据包含多个测试实例,每组数据由两行组成,第一行是n和x,第二行是已经有序的n个整数的数列。
Output
对于每个测试实例,请找出序列中第1个大于x的数的下标!。
Sample Input
3 3
1 2 4
Sample Output
2

#include <bits/stdc++.h>

using namespace std;
int a[1000005];
int main()
{
    int n,k;
    while(scanf("%d%d",&n,&k)!=-1)
    {
        int i;
        for(i=0;i<n;i++)
            scanf("%d",&a[i]);
        int ans;
        ans=upper_bound(a,a+n,k)-a;
        printf("%d\n",ans);
        memset(a,0,sizeof(a));
    }
    return 0;
}

题目二:小清新的二分查找之旅

Description
小清新又在疯狂懵逼了,遇到了一道题,并且发誓绝对不会告诉别人:在题号899的题目上脸懵逼了好久,于是他决定强化一下题目900,以抒发心中的抑郁之气。所以……
给出一组整数,整数个数为n,n不超过1,000,000,问这组整数中是否有k,总共询问q次。
Input
多组输入。
每组输入输入:
第一行2个整数:n q ,1 <=q , n <= 1,000,000 。
第二行 有 n 个整数,已升序排序。所有数 <= 1 e 9+7.
第三行 有 q 个整数,用于查询。
每行各数据之间有空格隔开。
Output
对每个查询数据分别输出一行
存在输出:
no
不存在输出:
YES
Sample Input
5 2
1 2 3 4 5
2 10
Sample Output
no
YES

#include <bits/stdc++.h>

using namespace std;
int a[1000005];
int main()
{
    int n,q;
    while(scanf("%d%d",&n,&q)!=-1)
    {
        int i;
        for(i=1;i<=n;i++)
            scanf("%d",&a[i]);
        for(i=1;i<=q;i++)
        {
            int t,cs;
            scanf("%d",&t);
            if(t==a[upper_bound(a+1,a+1+n,t)-a-1])
                printf("no\n");
            else
                printf("YES\n");
        }
    }
    return 0;
}

题目三:小清新的函数坐标-二分

Description
一天,小清新用一些奇奇怪怪的工具绘制函数图像,玩得不亦乐乎,期间发现了一个函数:
F(x) = 0.0001 x^5 + 0.003 x^3 + 0.5 x - 3 ,聪明的他一眼see穿了它的单调性,现在,小清新想标注一些点,已经写出了它们的 y 坐标值 ,聪明的你能帮助 可爱善良的小清新把对应的 x 坐标值找出来么。谢谢啦!
保证: -20 < x < 20 。答案精确到小数点后第4位。数据多组输入形式。
Input
(多组输入)每行一个实数 y
Output
每行一个四位小数 x
Sample Input
-356.957952
350.957952
Sample Output
-19.9995
19.9995

#include <bits/stdc++.h>
using namespace std;
int main()
{
    double x,y;
    while(scanf("%lf",&y)!=-1)
    {
        double low=-20,high=20,mid,jl=99999;
        while(low<=high)
        {
            mid=low+(high-low)/2;
            if(abs(mid-jl)<0.00001)
                break;
            if(0.0001*mid*mid*mid*mid*mid+0.003*mid*mid*mid+0.5*mid-3<y)
                low=mid;
            if(0.0001*mid*mid*mid*mid*mid+0.003*mid*mid*mid+0.5*mid-3>y)
                high=mid;
            jl=mid;
        }
        printf("%.4lf\n",mid);
    }
    return 0;
}

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

Description
小清新又要使坏了,他发现二倍问题(Problem 8)并不能难住大家,于是他决定悄咪咪的改一波数据。
于是:
给定2到10,000个不同的正整数,你的任务是计算这些数里面有多少个数对满足:数对中一个数是另一个数的两倍。比如给定1 4 3 2 9 7 18 22,得到的答案是3,因为2是1的两倍,4是2个两倍,18是9的两倍。
Input
输入包括n组测试数据。第一行一个正整数 n 。
接下来n行表示n组数据,每组数据为一行,给出2到10,000个两两不同且小于100,000的正整数。每一行最后一个数是0,表示这一行的结束后,这个数不属于那2到10,000个给定的正整数。
Output
对每组输入数据,输出一行,给出有多少个数对满足其中一个数是另一个数的两倍。
Sample Input
3
1 4 3 2 9 7 18 22 0
2 4 8 10 0
7 5 11 13 1 3 0
Sample Output
3
2
0

#include <bits/stdc++.h>

using namespace std;
int a[200005]={0};
int main()
{
    int n;
    scanf("%d",&n);
    while(n--)
    {
        int t,i=0,flag=0,maxx=0;
        do
        {
            scanf("%d",&t);
            if(t==0)
                break;
            a[t]++;
            if(a[t*2]==1)
                flag++;
            if(t%2==0&&a[t/2]==1)
                flag++;
        }while(1);
        printf("%d\n",flag);
        memset(a,0,sizeof(a));
    }
    return 0;
}

题目五:简单几何-二分

Description
一个长方体体积为v1,长为r,高为h,宽为1。一个圆柱体体积为v2,底面半径为r,高为h。r的取值范围为0<r<=100000,h的取值范围为1<=h<=100000的整数。给出h求使v2与v1的差值小于等于r^π的r的最小值
Input
输入第一行为一个整数t,表示接下来有t组数据。第二行到第t+1行,每行一个整数表示h
Output

输出r并保留4位小数
Sample Input
4
1
2
3
4
Sample Output
2.4073
4.7047
6.8441
8.8921

#include <bits/stdc++.h>

using namespace std;
double p=acos(-1.0);
int h;
int f(double mid)
{
    if(p*mid*mid*h-mid*h<=pow(mid,p))
        return 1;
    else
        return 0;
}
int main()
{
    int t;
    scanf("%d",&t);
    while(t--)
    {
        scanf("%d",&h);
        double low=0,high=100000,mid;
        while(low<high)
        {
            mid=(low+high)/2.0;
            if(high-low<1e-8)
                break;
            if(f(mid))
                high=mid;
            else
                low=mid;
        }
        printf("%.4lf\n",mid);
    }
    return 0;
}

题目六:小清新切绳子-二分

Description
小清新又双叒叕被WA疯了,原来是被double类型的精度给坑了,但题目那么好,怎么能不安利一波呢=。=
于是他悄悄地修改了下数据,安利给那些可爱的小可爱们(没错( ̄▽ ̄)~*),就是屏幕前的你们。
有N条绳子,它们的长度分别为Li。如果从它们中切割出K条长度相同的绳子,这K条绳子每条最长能有多长?
Input
多组输入!
第一行两个整数N和K,接下来一行N个整数,描述了每条绳子的长度Li ,以空格隔开。
对于100%的数据 1<=Li<10,000,000 , 1<=n,k<=10000
Output
切割后每条绳子的最大长度(一个整数)
Sample Input
4 11
802 743 457 539
Sample Output
200


#include <bits/stdc++.h>

using namespace std;
int k;
int chick(int x,int a[],int n)
{
    int i;
    int sum=0;
    for(i=1; i<=n; i++)
    {
        sum+=a[i]/x;
    }
    if(sum>=k)
        return 1;
    else
        return 0;
}
int b[10000001];
int main()
{
    int n,a[10005];
    while(scanf("%d%d",&n,&k)!=-1)
    {
        int i,maxx=0;
        for(i=1; i<=n; i++)
        {
            scanf("%d",&a[i]);
            maxx=maxx>a[i]?maxx:a[i];
        }
        int low=0,high=maxx,mid,ans=0;
        while(low<=high)
        {
            mid=(low+high)/2;
            if(chick(mid,a,n)==1)
                ans=mid,low=mid+1;
            else
                high=mid-1;
        }
        printf("%d\n",ans);
    }
    return 0;
}

题目七:卖古董-DP-二分

Description
你的朋友小明有n个古董,每个古董的价值给出,然后小明要在接下来的m天内将所有古董依次卖出(注意:必须依次卖出,也就是从第一个开始卖卖到最后一个),小明希望
的是这m天内每天卖出的价值和的最大值最小,你来帮助他把?
Input
一共T组数据,每组一个n和m,代表n个古董以及m天(1<=m<=n),然后下面n行为古董的价值,古董价值为1到10000(1<=n<=100000)
Output
输出m天内卖出的古董价值和的最大值(当然是最优的时候),我们希望的是这个最大值越小越好
Sample Input
3
7 5
100
400
300
100
500
101
400
4 3
2
6
2
4
4 2
2
6
2
4
Sample Output
500
6
8

#include <bits/stdc++.h>

using namespace std;
int k,n,a[10005];
int check(int x)
{
    int sum=0;
    int i,flag=1;
    for(i=1;i<=n;i++)
    {
        if(a[i]>x)
            return 0;
        sum+=a[i];
        if(sum>x)
        {
            flag++;
            sum=a[i];
        }
    }
    if(flag<=k)
        return 1;
    else
        return 0;
}
int main()
{
    int T;
    scanf("%d",&T);
    while(T--)
    {
        scanf("%d%d",&n,&k);
        int i,sum=0;
        for(i=1;i<=n;i++)
        {
            scanf("%d",&a[i]);
            sum+=a[i];
        }
        int low=0,high=sum,mid,ans=0;
        while(low<=high)
        {
            mid=(low+high)>>1;
            if(check(mid)==1)
            {
                ans=mid;
                high=mid-1;
            }
            else
                low=mid+1;
        }
        printf("%d\n",ans);
    }
    return 0;
}

题目八:切绳子实数版-二分

Description
有N条绳子,它们的长度分别为Li。如果从它们中切割出K条长度相同的
绳子,这K条绳子每条最长能有多长?答案保留到小数点后2位。
Input
第一行两个整数N和K,接下来N行,描述了每条绳子的长度Li。
Output
切割后每条绳子的最大长度。
Sample Input
4 11
8.02
7.43
4.57
5.39
Sample Output
2.00

#include <bits/stdc++.h>

using namespace std;
int k,n,a[100005];
int check(int x)
{
    int sum=0;
    int i,flag=1;
    for(i=1; i<=n; i++)
    {
        if(a[i]>x)
            return 0;
        sum+=a[i];
        if(sum>x)
        {
            flag++;
            sum=a[i];
        }
    }
    if(flag<=k)
        return 1;
    else
        return 0;
}
int main()
{
    while(scanf("%d%d",&n,&k)!=-1)
    {
        int i,sum=0;
        for(i=1; i<=n; i++)
        {
            scanf("%d",&a[i]);
            sum+=a[i];
        }
        int low=0,high=sum,mid,ans=0;
        while(low<=high)
        {
            mid=(low+high)>>1;
            if(check(mid)==1)
            {
                ans=mid;
                high=mid-1;
            }
            else
                low=mid+1;
        }
        printf("%d\n",ans);
    }
    return 0;
}

题目九:数列分段-二分

Description
对于给定的一个长度为N的正整数数列A-i,现要将其分成M(M≤N)段,并要求每段连续,且每段和的最大值最小。

关于最大值最小:

例如一数列4 2 4 5 1要分成3段

将其如下分段:

[4 2][4 5][1]
第一段和为6,第2段和为9,第3段和为1,和最大值为9。

将其如下分段:

[4][2 4][5 1]
第一段和为4,第22段和为6,第33段和为6,和最大值为6。

并且无论如何分段,最大值不会小于6。

所以可以得到要将数列4 2 4 5 1要分成3段,每段和的最大值最小为6。
Input
第11行包含两个正整数N,M。

第22行包含N个空格隔开的非负整数A_i
含义如题目所述。
Output
一个正整数,即每段和最大值最小为多少。
Sample Input
5 3
4 2 4 5 1
Sample Output
6

#include <bits/stdc++.h>

using namespace std;
int k,n,a[100005];
int check(int x)
{
    int sum=0;
    int i,flag=1;
    for(i=1; i<=n; i++)
    {
        if(a[i]>x)
            return 0;
        sum+=a[i];
        if(sum>x)
        {
            flag++;
            sum=a[i];
        }
    }
    if(flag<=k)
        return 1;
    else
        return 0;
}
int main()
{
    while(scanf("%d%d",&n,&k)!=-1)
    {
        int i,sum=0;
        for(i=1; i<=n; i++)
        {
            scanf("%d",&a[i]);
            sum+=a[i];
        }
        int low=0,high=sum,mid,ans=0;
        while(low<=high)
        {
            mid=(low+high)>>1;
            if(check(mid)==1)
            {
                ans=mid;
                high=mid-1;
            }
            else
                low=mid+1;
        }
        printf("%d\n",ans);
    }
    return 0;
}

题目十:二分查找加强版

Description
有n(1<=n<=2000005)个整数,是乱序的,现在另外给一个整数x,请找出序列排序后的第1个大于x的数的下标!
Input
输入数据包含多个测试实例,每组数据由两行组成,第一行是n和x,第二行是已经有序的n个整数的数列。
Output
对于每个测试实例,请找出从小到大排序后的序列中第1个大于x的数的下标!。
Sample Input
3 3
1 4 2
Sample Output
2

#include <bits/stdc++.h>

using namespace std;
int a[2000005];
int main()
{
    int n,x;
    while(cin >> n >> x)
    {
        int i;
        for(i=0;i<n;i++)
        {
            cin >> a[i];
        }
        sort(a,a+n);
        int flag;
        flag=upper_bound(a,a+n,x)-a;
        cout << flag << endl ;
    }
    return 0;
}
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值