//二分查找总结:由于本人二分常年写成死循环
/*
简介:二分查找 == 折半查找
要求:线性表,有序表(注意升序与降序)
思想:
设查找区间[L,R]
取中点 mid = (L+R)/2
判定mid是否符合要求:(如何判断:bool check(int mid); )
是:缩短区间求边界;直接返回;
否:缩短区间
最终结果val = R 或者val = L;
*/
//典型线性表查找数据
int er_search(int a[],int n, int key)
{
const int inf = 0x3f3f3f3f;
int L=0,R=n;
while(L<R){
int mid = (L+R)/2;
if(key==a[mid]){
return mid;
}
else if(key<a[mid]){
R=mid-1;
}
else if(k>a[mid]){
L=mid+1;
}
}
return inf;
}
//CSDN某博客对二分的64种分类
/*
*取整方式
向下取整(最小值) 向上取整(最大值)
*区间开闭
闭区间 左闭右开区间 左开右闭区间 开区间
*问题类型
单增
对于不下降序列a,求最小的i,使得a[i] = key
对于不下降序列a,求最大的i,使得a[i] = key
对于不下降序列a,求最小的i,使得a[i] > key
对于不下降序列a,求最大的i,使得a[i] < key
单减
对于不上升序列a,求最小的i,使得a[i] = key
对于不上升序列a,求最大的i,使得a[i] = key
对于不上升序列a,求最小的i,使得a[i] < key
对于不上升序列a,求最大的i,使得a[i] > key
*/
//下面四个不下降的例子
//a[] = 1 2 3 4 5 6 6 6 7 9
//min i,a[i] = key; =>a[5]
while(s < e){
mid = (e+s)/2;// 向下取整
if(key <= a[mid])
e = mid;
else
s = mid + 1;
}
//max i,a[i] = key =>a[7]
while(s < e){
mid = (e+s+1)/2;// 向上取整
if(key >= a[mid])
s = mid;
else
e = mid - 1;
}
//min i, a[i] > key =>=>a[8]
while(s < e){
mid = (e+s)/2;//向下取整
if(key < a[mid])
e = mid;
else
s = mid + 1;
}
// max i, a[i] < key =>a[4]
while(s < e){
mid = (e+s+1)/2;//向上取整
if(key > a[mid])
s = mid;
else
e = mid - 1;
}
/*巧记,但不是完全正确
循环:L<R
求mid时:求max :L+R+1 求min: L+R;
if():真实值与猜测值的关系作为条件:max-真实大于猜测 min-真实小于猜测
防死循环:调整if下的L或者R 另一个边界在else下注意+-1;
总结:循环L<R mid注意1 else下防死循环
*/
//另一种简单分类:第一个大于v,第一个大于等于v,最后一个小于v,最后一个小于等于v
/*内容来自:http://www.cnblogs.com/xiaowuga/p/8604750.html
第一个大于等于v:
lower_bound(ForwardIterator first, ForwardIterator last,const T& val, Compare comp)
我们假设L为当前区间的答案,R为当前区间的实际答案(因为R是第一个大于等于v的下标),
我们每次二分的实际上是为了让L和R不断靠近,
所以当L==R的时候,我们假设的答案等于实际的答案,那么就结束循环了,返回答案L。
while(L<R){
int M=(L+R)/2;
if(a[M]>=v) R=M;//R是第一个大于等于v下标,那么R最大只能是m
else L=M+1;//[M,R)区间内的下标都是小于v的,L作为最后的答案最小只能是M+1
}
第一个大于v:
upper_bound(ForwardIterator first, ForwardIterator last,const T& val, Compare comp);
我们假设L为当前区间的答案,R为当前区间的实际答案(因为R是第一个大于v的下标),
我们每次二分的实际上是为了让L和R不断靠近,
所以当L==R的时候,我们假设的答案等于实际的答案,那么就结束循环了,返回答案L。
while(L<R){
int M=(L+R)/2;
if(a[M]>v) R=M;//R是第一个大于v下标,那么R最大只能是M
else L=M+1;//[M,R)区间内的下标都是小于等于v的,L作为最后的答案最小只能是M+1
}
最后一个小于等于v
我们假设R为当前区间的答案,L为当前区间的实际答案(因为L是最后一个小于等于v的下标),
我们每次二分的实际上是为了让L和R不断靠近,
所以当L==R的时候,我们假设的答案等于实际的答案,那么就结束循环了,返回答案L。
while(L<R){
int M=(L+R+1)/2;
if(a[M]<=v) L=M;//L是最后一个小于等于v下标,那么L最小只能是M。
else R=M-1;//(L,M]区间内的下标都是大于v的,R作为最后的答案最大只能是M-1。
}
最后一个小于v
我们假设R为当前区间的答案,L为当前区间的实际答案(因为L是最后一个小于v的下标),
我们每次二分的实际上是为了让L和R不断靠近,
所以当L==R的时候,我们假设的答案等于实际的答案,那么就结束循环了,返回答案L。
while(L<R){
int M=(L+R+1)/2;
if(a[M]<v) L=M;//L是最后一个小于v下标,那么L最小只能是M。
else R=M-1;//(L,M]区间内的下标都是大于等于v的,R作为最后的答案最大只能是M-1。
}
*/
//二分条件搜索的应用:最大化最小值,最小化最大值,最大化平均值
/*最大化最小值
边界属性: 正确答案ans
满足条件但不是最大:ans-1
不满足条件:ans+1
二分类型:最后一个满足条件的ans值
思路: 把条件看成具体的值
求最后一个小于等于v
*/
/* 最小化最大值
边界属性: 正确答案ans
满足条件但不是最小:ans+1
不满足条件:ans-1
二分类型:第一个满足条件的值
思路: 把条件看成具体的值
求第一个大于等于v
*/
/*最大化平均值:
模型构建:
有n个物品,每个物品分别对应一个重量w和价值v。要求选出k个,使得平均每单位重量的价值最大。
二分模型:搜索满足条件check(mid)的最大解
check()模型:
构建过程:
单个物品:
平均价值:v/w
判断这个物品是否被挑选(是否满足单位价值mid) v/w>=mid
化简得 v-wx>=0; 可记录c[i] = v-wx(第i个)
多个物品:
循环所有物品
k个物品
求和c[0]--c[k-1]
如果这个和大于零,说明总体的平均值大于mid
模型:二分查找最大化平均值
int n;数据组数
int k;要求件数
bool check(double mid){
c[i] = v[i]-mid*w[i];
sort(c,c+n,cmp);//逆序
double sum=0;
sum+=c[0] ---c[k-1]
return sum>=0;//sum = 0时候是正好满足条件
}
*/