二分答案类型的题在之前不经常做所以尽管训练题比较简单,但还是做起来有点吃力。
二分答案顾名思义就是通过不断二分检验所有可能的答案值是否满足题目条件,直到选出我们需要的答案(个人理解。。。)
个人感觉二分答案问题都是在研究的是某种意义上的最大 ,最小值问题。
例题1:
poj 1064——Cable master
题意:给n段长度不同的电缆,要将他们均分成k段,问每段最长能分成多少。
思路:首先我们可以先判断最长长度的边界是多少,显然每段最小就是0,最大不会超过所有电缆长度和。然后再二分这个区间,判断以当前长度标准去截电缆能截的段数是否大于等于k,然后再判断保留左区间还是右区间。
代码:
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <cmath>
using namespace std;
#define int long long
double cnt[100005]; int n,k;
bool check(double mid){
int ret=0;
for(int i=0;i<n;i++) ret+=(int)(cnt[i]/mid);
if(ret>=k) return true;
else return false;
}
signed main(){
cin>>n>>k; double sum=0;
for(int i=0;i<n;i++){
cin>>cnt[i]; sum=max(sum,cnt[i]);
}
double lf=0,rt=sum;
while(rt-lf>1e-3){//厘米精度所以要分到小数点后三位
double mid=(rt+lf)/2;
if(check(mid)) lf=mid;
else rt=mid;
}
printf("%.2f\n",floor(rt*100)/100);
return 0;
}
例题2:Median
题意:给出n个数,要找出n个数中两两差值绝对值的中位数,如果差值个数是偶数的话就选出中间两个数中较小的数,如:n为4的话,两两差值就会出现6个数,那我们就要找出6个数中第3小的数。
思路:这个题明显难度上升。首先我们要把这个题转化为一个二分答案的题,要找出中位数可以理解为找出所有可能答案值之中,小于等于该答案值的数的个数大于等于n*(n-1)/4(差值个数的一半)中的最小答案值。那我们可以一套流程a掉这道题。找边界:最小为0,最大不会超过差值最大值。将二分的每个答案check一下判断小于等于该答案的差值的个数是否大于等于n*(n-1)/4即可。
代码:
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <vector>
using namespace std;
#define int long long
int cnt[100005];
vector<int> m;
signed main(){
ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
int n;
while(cin>>n){
for(int i=1;i<=n;i++) cin>>cnt[i];
sort(cnt+1,cnt+n+1);
int lf=0,rt=cnt[n]; int k=n*(n-1)/4;
if((n*(n-1)/2)%2==1) k++; int ans=0;
if(n==1){
cout<<0<<endl; continue;
}
while(rt>=lf){
int mid=(rt+lf)>>1;
int sum=0; int j=1;
for(int i=2;i<=n;i++){//check当前的mid
while(cnt[i]-cnt[j]>mid) j++;
sum+=(i-j);
}
if(sum>=k){
ans=mid; rt=mid-1;
}
else lf=mid+1;
}
cout<<ans<<endl;
}
return 0;
}