要说二分答案,首先要从二分说起,首先先上题:
题目简单的来讲,就是有一个由小到大的数列,从中搜索一个数的下标,我们先来看一下二分的模板:
while(start<end){
mid=start+(end-start)/2;
if(num[mid]>=s){ //这里的s就是我们要搜索的数
end=mid;
}else{
start=mid+1;
}
}
就是我们从这个数列的中间这个数开始比较,是中间这个数大还是我们要找的这个数大
那么现在就有三种情况:
中间的数=要找的数 | 向数列的左边搜索,因为要找到第一次出现这个数的下标 |
中间的数>要找的数 | 向数列左边搜索 |
中间的数<要找的数 | 向数列右边搜索 |
这个时候我们不断去重复上述操作:
我们用start来存储这个数列开始的坐标,end来存储这个数列结束的坐标,mid来存储这个数列中间这个数的坐标。
注意:找mid的值我们最好使用start+(end-start)/2而不是(start+end)/2,因为假如这个数列很大,start+end有可能直接爆了。
那么最后我们就可以找到这个数的下标了,我们要这个数的下标如果简单的遍历,那么时间复杂度就是O(nm),用二分就可以把时间缩短到O(nlogm)
接下来就是程序完整代码:
#include<bits/stdc++.h>
using namespace std;
int num[1000005],m,n;
int check(int s){
int start=1,end=m,mid=0;
while(start<end){
mid=start+(end-start)/2;
if(num[mid]>=s){
end=mid;
}else{
start=mid+1;
}
}
if(num[start]==s){
return start;
}else{
return -1;
}
}
int main(){
cin>>m>>n;
int t=0;
for(int i=1;i<=m;i++){
cin>>num[i];
}
for(int i=1;i<=n;i++){
cin>>t;
cout<<check(t)<<' ';
}
return 0;
}
那么接下来,我们在知道了二分搜索的前提下,我们来看一看二分答案,二分答案的实质就是把前面二分搜索的数列变为我们答案的所有可能性,mid就是我们尝试的答案,二分答案的前题就是答案具有单调性,直接将有点难以理解,还是直接上题:
首先是看一下题目,是符合单调性的,然后这些长度都是两位小数,非常不方便我们二分,这边就把所有的长度都*100,最后把答案再/100,就可以避免了。
我们这里假设,绳子最长是题目里给的100000.00,*100后就是10000000,这就是答案的最大值,不可能会比这个大,那么答案的最小值就是0,所以start=0,end=10000000,所以我们这里把这个数组开始二分,那么现在的难点就是验证这个答案能不能取到。
我们现在用每一个长度去除以mid,把每一个段数加起来,然后把这个总段数和我们要的段数K进行比较,我们假设这个总段数为num,结论如下:
num>K | 向右搜索 |
num=K | 向右搜索 |
num<K | 向左搜索 |
向右搜索,我们就把start=mid
向左搜索,我们就把end=mid
然后不断收缩,这个mid/100就是最终的答案,直接上代码:
#include<bits/stdc++.h>
using namespace std;
double num;
double a[1000005];
double check(int s,int s1){
int all=0;
for(int i=0;i<num;i++){
all+=(a[i]/s);
}
if(all>=s1){
return 1;
}else{
return 0;
}
}
int main(){
double s;
cin>>num>>s;
long long end=10000000000,start=1,mid=0,ans=0;
for(int i=0;i<num;i++){
cin>>a[i];
a[i]=a[i]*100;
}
while(start<=end){
mid=(int)(start+end)/2;
if(check(mid,s)){
start=mid+1;
ans=mid;
}else{
end=mid-1;
}
}
printf("%.2lf",(double)ans/100);
return 0;
}