二分算法可以分为二分查找和二分答案(主要考察二分答案)
二分算法是:在一个广义的单调有序(递增或者递减)的区间 [ a1,an ] 中查找元素x,每次将区间分为左右长度相等的两部分,通过比较x与分界点的大小关系,判断x在哪个区间中并调整区间的上下界,重复直至找到x。
时间复杂度为O(logn)优于顺序查找,所以经常用于对大量数据处理。
作用 :
1.查找元素是否存在
2.求满足条件的最值
二分查找
函数
查找是否存在
#include<algorithm>
binary_search(arrfist,arrlast,value)
arrfist: 数组首地址 arrlast: 数组末地址后一个位置 value : 需要查找的值
函数功能 :在数组中以二分法检索的方式查找,若在数组(要求数组元素非递减)中查找到value元素则返回真,若查找不到则返回值为假。
查找大于等于查找数的位置
lower_bound(arrfist , arrlast, value)//大于等于
lower_bound(arrfist , arrlast, value, greater<T>()) (返回小于或等于value的)
arrfist: 函数首地址 arrlast: 函数为地址 value : 需要查找的值
函数功能:lower_bound()在first和last中的前闭后开区间进行二分查找,
返回指向大于或等于value的第一个元素的指针。
如果所有元素都小于val,则返回last
查找大于查找数的位置
upper_bound(arrfist , arrlast, value)
upper_bound(arrfist , arrlast, value, greater<T>()) (返回小于value的)
arrfist: 函数首地址 arrlast: 函数为地址 value : 需要查找的值
函数功能: 函数upper_bound()在first和last中的前闭后开区间进行二分查找,
返回指向大于value的第一个元素的指针。
如果所有元素都小于val,则返回last
二分答案
算法核心思维就是利用二分法在一段区间内快速找到符合给定条件的最大值或是最小值。
因为我们已经知道答案一定在这个区间。
用于高效解决这么两种问题:
-
最大化最小值:求解一个满足条件的答案x,x值域为有限的[l,r],当x很小的时候都是符合条件的,我们需要求满足条件的最大的x.
-
最小化最大值:求解一个满足条件的答案x,x值域为有限的[l,r],当x很大的时候都是符合条件的,我们需要求满足条件的最小的x.
-
最大化最小值:
(1).我们定义两个整数l,r,赋值l一个必定小于答案最小值的数,赋值r一个必定大于答案最大值的数.
(2).当答案区间[ l, r ]不够小时,不断执行步骤(3)
(3).每次循环取mid=(l+r+1)/2;判断答案为mid时是否符合条件,符合则令l=mid,否则r=mid-1,这样答案区间[ l,r ]一定会以logn的速度快速缩小.
(4).循环结束后的 l 即为答案 -
最小化最大值:
(1). 我们定义两个整数l,r,赋值l一个必定小于答案最小值的数,赋值r一个必定大于答案最大值的数. (2).当答案区间[l,r ]不够小时,不断执行步骤(3)
(3).每次循环取mid=(l+r)/2;判断答案为mid时是否符合条件,符合则令r=mid,否则l=mid+1,这样答案区间[l,r]一定会以logn的速度快速缩小.
(4).循环结束后的 l 即为答案
整数
最小化最大值问题
模板
while (l < r)//往左找答案 最小化最大值
{
int mid = l + r >> 1; //(l+r)/2
if (check(mid)) r = mid; // check()判断mid是否满足性质,满足时则往左搜索
else l = mid + 1;
}
例题
题意:
有n个城市,b个箱子,每个箱子可以装无限信封,问装的最多的信箱最少可以装多少信
思路:
最大值最小化
直接二分信封的数量,然后用每个城市的信封数整除该数量,判断最后的信箱数量是否
满足题意所给即可
代码
#include<iostream>
#include<algorithm>
using namespace std;
const int maxn = 1e6 + 5;
int n,m,l,r,mid;
int a[maxn];
bool check(int x){
int sum=0;
for(int i=0;i<n;i++){
if(a[i]%x==0){
sum+=a[i]/x;
}
else sum+=a[i]/mid+1;
}
return sum<=m;
}
int main() {
//ios;
while(scanf("%d %d",&n,&m)&&n!=-1&&m!=-1){
l=1,r=0;
for(int i=0;i<n;i++){
scanf("%d",&a[i]);
r=max(a[i],r);
}
while(l<r){
mid=(l+r)>>1;
if(check(mid)){
r=mid;
}
else l=mid+1;
}
printf("%d\n",l);
}
return 0;
}
最小值最大化问题
模板
while (l < r)//往右找答案 最大化最小值
{
int mid = (l + r + 1 )>> 1; //(l+r+1)/2
if (check(mid)) l = mid;//check()判断mid是否满足性质,满足时则往右搜索
else r = mid - 1;
}
例题
题意:
有N个牛棚,每个牛棚位置已知,有C头奶牛,奶牛会相互攻击,问两头奶牛的
最小距离的最大值是多少?
思路:
二分最大的最小距离
判断条件 : 如果相邻两个距离大于mid,则满足该距离的牛棚数量加一,判断最后满足
条件的数量是否满足牛的个数即可
代码
#include<iostream>
#include<algorithm>
using namespace std;
typedef long long ll;
const int maxn = 1e6 + 5;
int n,c;
ll a[maxn];
ll l,r,mid;
bool check(ll x){
ll com=1;
ll e=a[0];
for(int i=1;i<n;i++){
if(a[i]-e>=x){
e=a[i];
com++;
}
if(com>=c){
return true;
}
}
return false;
}
int main() {
scanf("%d %d",&n,&c);
for(int i=0;i<n;i++){
scanf("%lld",&a[i]);
}
sort(a,a+n);
l=0,r=a[n-1]-a[1];
while(l<r){
mid=(r+l+1)>>1;
if(check(mid)){
l=mid;
}
else r=mid-1;
}
printf("%lld\n",r);
return 0;
}
浮点数
最小化最大值
模板
while (r-l>0.000001)//往左找答案 最小化最大值
{//保留4位小数
double mid = (l + r) /2; //(l+r)/2.0000
if (check(mid)) r = mid-0.000001; // check()判断mid是否满足性质,满足时则往左搜索
else l = mid + 0.000001;
}
cout<<l<<endl;
最大化最小值
模板
while (r-l>0.000001)//往右找答案 最大化最小值
{
double mid = (l + r + 0.00001 )>> 1; //(l+r+1)/2
if (check(mid)) l = mid+0.00001;//check()判断mid是否满足性质,满足时则往右搜索
else r = mid - 0.00001;
}
cout<<l<<endl;
通解
模板
#define E 1e-6
while(r-l>=E){
mid=(l+r)/2;
if(check(mid)){
l=mid;
}
else r=mid;
}
例题
题意:
一个小朋友生日和他的小伙伴们分一个蛋糕,总人数是f+1。保证每个人的体积相
等,求每个人能得到的最大的蛋糕的体积(保留4位小数)
思路:
浮点数二分
用蛋糕的体积整除人数,判断是否满足题意的人数要求
代码
#include<iostream>
#include<math.h>
#define PI acos(-1.0)
#define E 1e-6
using namespace std;
const int maxn = 1e5 + 5;
int n,f;
double a[maxn];
double l,r,mid,max1=0;
bool check(double x){
int sum=0;
for(int i=0;i<n;i++){
sum+=(int)(a[i]/x);
}
return sum>=f+1;
}
int main() {
int t;
scanf("%d",&t);
while(t--){
scanf("%d %d",&n,&f);
for(int i=0;i<n;i++){
scanf("%lf",&a[i]);
a[i]=a[i]*a[i]*PI;
max1=max(a[i],max1);
}
l=0,r=max1;
while(r-l>=E){
mid=(l+r)/2;
if(check(mid)){
l=mid;
}
else r=mid;
}
printf("%.4f\n",l);
}
return 0;
}