您的位置:首页 > 其它

二分搜索

2018-03-17 15:33 155 查看
* 二分搜索法,是通过不断缩小可能存在的范围,从而求得问题最优解的方法。

下面介绍经典的二分搜索法问题:

一.从有序数组中查找某个值lower_bound
给定长度为n的单调不下降数列a0····an-1和一个数 k,求满足ai ≥ k 条件的最小的i 。不存在的情况下输出n。
 1 ≤ n ≤ 10^6
 0 ≤ a0 ≤a1 ≤ ···≤an-1≤ 10^9 
0 ≤ k ≤ 10^9
输入:                                                                                                                                                                                         
n = 5 
a = { 2, 3 , 3 , 5 , 6 } 
k=3
输出:
1( a0  < 3 , a1 >= 3)
分析:如果朴素地按照顺序逐个查找的话,也可以求得答案,但是当数列长度很长时,这种方法显然很费时。但是如果利用数列的有序性这一条件,则可以得到更高效的算法。首先先看一下第n/2个值,如果a [n/2] ≥ k的话,就可以知道解不大于n/2。反之,如果 a [n/2] ≤ k ,就可以知道解大于n/2,通过这一次的比较,可以把解的范围缩小一半。反复与区间的中点进行比较,这样就可以不断地把范围缩小到原来的一半,最终在O(logn)次的比较之内求得最终的解。
思想转化为代码://输入

int n,k;
int a[MAX_N];

void solve()
{
int lb = -1 , up = n;//初始化接的存在范围 lb(下界), ub(上界)
while(ub-lb > 1) //重复循环知道解的范围不大于1
{
int mid = (lb+ub)/2;
if(a[mid]>=k) // 如果mid满足条件,则解的存在范围变为(lb,mid]
ub = mid;
else // 如果mid不满足条件,则解的存在范围变为(mid,ub]
lb = mid;
}
printf("%d",ub);
}这种算法被称为二分搜索。此外,STL以lower_bound函数形式实现了二分搜索。这个算法除了在有序数列查找值之外,在求最优解的问题上也非常有用。                                                                                                                                                    
模板题:                                                                                                                                                                                              
让我们考虑一下“求满足某个条件C(x)的最小值x”这一问题。对于任意满足C(x)的x,如果所有x' ≥ x 也满足C(x)的值,我们就可以用二分搜索来求得最小的x。
首先我们将区间的左端点初始化为不满足C(x)的值,右端点初始化为满足C(x)的值。然后每次取中点mid= ( lb + ub ) / 2 ,判断C(mid)是否满足并缩小范围,直到( lb , ub ]足够小了为止,最后ub就是要求的最小值。最大化的问题也可以用同样的方法求解。

二. 假定一个解判断是否可行           

 Cable master POJ - 1064 

Inhabitants of the Wonderland have decided to hold a regional programming contest. The Judging Committee has volunteered and has promised to organize the most honest contest ever. It was decided to connect computers for the contestants using a "star" topology - i.e. connect them all to a single central hub. To organize a truly honest contest, the Head of the Judging Committee has decreed to place all contestants evenly around the hub on an equal distance from it.To buy network cables, the Judging Committee has contacted a local network solutions provider with a request to sell for them a specified number of cables with equal lengths. The Judging Committee wants the cables to be as long as possible to sit contestants as far from each other as possible.The Cable Master of the company was assigned to the task. He knows the length of each cable in the stock up to a centimeter,and he can cut them with a centimeter precision being told the length of the pieces he must cut. However, this time, the length is not known and the Cable Master is completely puzzled.You are to help the Cable Master, by writing a program that will determine the maximal possible length of a cable piece that can be cut from the cables in the stock, to get the specified number of pieces.
InputThe first line of the input file contains two integer numb ers N and K, separated by a space. N (1 = N = 10000) is the number of cables in the stock, and K (1 = K = 10000) is the number of requested pieces. The first line is followed by N lines with one number per line, that specify the length of each cable in the stock in meters. All cables are at least 1 meter and at most 100 kilometers in length. All lengths in the input file are written with a centimeter precision, with exactly two digits after a decimal point.

OutputWrite to the output file the maximal length (in meters) of the pieces that Cable Master may cut from the cables in the stock to get the requested number of pieces. The number must be written with a centimeter precision, with exactly two digits after a decimal point.If it is not possible to cut the requested number of pieces each one being at least one centimeter long, then the output file must contain the single number "0.00" (without quotes).Sample Input4 118.027.434.575.39Sample Output2.00
题意:有N条绳子,他们的长度分别为Li。如果从他们中切割出K条长度相同的绳子的话,这K条绳子每条最长能有多长?
分析: 这可以看成一个最大化问题,所以用二分搜索会比较容易得到答案。套用一下模板:           令 C(x) : 可以得到k段绳长为x的绳子           问题:求满足C(X)的最大的x           区间初始化:lb=0 ,ub=INF                                                                             

思路转化为代码:
                                                                                            
//输入
int N,K;
double L[MAX_N]

int C(int x) // 判断是否满足条件
{
int num;
for(i=0;i<n;i++)
num += (int)(L[i]/x);
return num >= K; //大于等于K返回1,否则返回0
}

void solve()
{
double lb=0,ub=INF;//区间初始化
for(int i=0;i<100;i++)
{
double mid=(lb+ub)/2;
if(C(mid))
lb=mid;
else
up=mid;
}
printf("%.2f\n",floor(ub*100)/100); //floor函数:向下取整


二分搜索法的结束判定:
在输出小数的问题中,一般都会指定允许的误差范围或者是指定输出中小数点后面的位数。因此在使用二分搜索法时,有必要设置合理的结束条件来满足精度的要求。在上面的程序中,我们指定了循环次数作为终止条件。1次循环可以把区间的范围缩小一半,100次的循环则可以达到10^-30的精度范围,基本上是没有问题的,除此之外,也可以把终止条件设为像(ub-lb)>EPS这样,制定一个区间的大小。在这种情况下,如果EPS取得太小了,就有可能会因为浮点小数精度的原因导致陷入死循环,请千万小心。
像这样,如果在求解最大化或最小化问题中,能够比较简单的判断条件是否满足,那么使用二分搜索法就可以很好的解决问题。
三.最大化最小值

Aggressive cows

 POJ - 2456Description
Farmer John has built a new long barn, with N (2 <= N <= 100,000) stalls. The stalls are located along a straight line at positions x1,...,xN (0 <= xi <= 1,000,000,000).

His C (2 <= C <= N) cows don't like this barn layout and become aggressive towards each other once put into a stall. To prevent the cows from hurting each other, FJ want to assign the cows to the stalls, such that the minimum distance between any two of them is as large as possible. What is the largest minimum distance?

Input
* Line 1: Two space-separated integers: N and C
* Lines 2..N+1: Line i+1 contains an integer stall location, xi

Output
* Line 1: One integer: the largest minimum distance
Sample Input
[/b]5 3
1
2
8
4
9

Sample Output
3
题意:农夫约翰搭了一间有N间牛舍的小屋。牛舍排在一条线上,第 i 号牛舍在 xi 的位置。但是他的 M 头牛对小屋很不满意,因此经常互相攻击。约翰为了防止牛之间互相伤害,因此决定把每头牛都放在离其他牛经可能远的牛舍。也就是要最大化最近的两头牛之间的距离。
分析:类似的最大化最小值或者最小化最大值的问题,通常用二分搜索就可以很好的解决 。套用模板:
           令C(X): 可以安排牛的位置使得任意的两头牛的距离不小于d
           问题:求满足C(d)的最大的d                                                   
           区间初始化:lb=0,ub=INF
 
思想转化为代码://输入

int N,M;
int x[MAX_N];

bool C(int d)
{
int last = 0;
for(int i =1 ;i < M ;i++)//放M头牛
{
int crt = last + 1;//crt
while(crt < N && x[crt]-x[last]<d)
crt++;
if(crt == N) return false;放到N为止,说明这M头牛放不下去
last = crt ;
}
return true;
}

void solve()
{
sort(x,x+N); //对x数组排序
int lb = 0; ub = INF; //区间初始化
while(ub-lb > 1)
{
int mid = (lb+ub) / 2;
if(C(mid)) //如果可以放下M头牛,说明当前的x有点小,让x变大再判断
lb=mid;
else //如果放不下,说明当前的x有点小,就先让x变小再判断
ub=mid;
}
printf("%d",lb);
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: