二分查找
使用二分查找可以使时间复杂度由n变为nlogn
但必须有条件 单调性 和 二段性
单调性是指答案区间的值从小到大或者从大到小
二段性指对于任何一个值 他的左半边必然成立 右半边必然不成立 或者 左半边必然不成立 右半边必然成立 根据题目具体分析
对于下题来说 2.50左边的所有绳子长度 都可以 但2.50是最大的 2.5右边均不成立
所以可以搜索所有答案 判断当前值成立与否 成立则右半边有可能有更优解 二分处理右半边
若不成立 则说明该点不正确 正确区间一定在其左半边 所以二分处理左半边。
#include<iostream>
using namespace std;
const int N = 1000010;
int n,m;
int a[N];
bool check(double mid){
int res=0;
for(int i=1;i<=n;i++){
res+=a[i]/mid;
// if(res>=m) return true;
}
// return false;
return res>=m;
}
int main(){
cin>>n>>m;
for(int i=1;i<=n;i++){
cin>>a[i];
}
double l=0,r=1e9;
while(r-l>1e-3){
double mid = l+(r-l)/2;
if(check(mid)) l=mid;
else r=mid;
}
printf("%.2lf\n",r);
return 0;
}
本题选自第八届蓝桥杯省赛AB组题目
二分思想类似 判断当前切出的长度能不能大于k 大于就说明当该点成立(因为有可能右更大块的巧克力也满足>=k) 右半边有可能有更优解
二分右半边 否则二分左半边 值得注意的是 当写出l=mid时 需要再l+r>>1左边再加一 防止死循环 大家可以模拟一下。
#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
const int N = 100010;
int h[N];
int w[N];
int n,k;
bool check(int mid){
int res=0;
for(int i=1;i<=n;i++){
res+=(h[i]/mid)*(w[i]/mid);
}
return res>=k;
}
int main(){
cin>>n>>k;
for(int i=1;i<=n;i++){
cin>>h[i]>>w[i];
}
int l=0,r=1e9;
while(l<r){
int mid = l+r+1>>1;
if(check(mid)) l=mid;
else r=mid-1;
}
cout<<l<<endl;
return 0;
}