题目来源于东北林业大学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;
}