今天整理一下有关二分查找入门的相关内容。
二分算法的实质就是通过改变查找区间,两边界逐步目标值靠拢,一步步缩小查找范围,最后锁定目标值。相比较于传统的遍历查找比较的算法,二分算法的时间复杂度只有O(Log N),而传统算法则是O(N),由此可见二分算法的便捷性和高效。
下面给道例题。
Every year the cows hold an event featuring a peculiar version of hopscotch that involves carefully jumping from rock to rock in a river. The excitement takes place on a long, straight river with a rock at the start and another rock at the end, L units away from the start (1 ≤ L ≤ 1,000,000,000). Along the river between the starting and ending rocks, N (0 ≤ N ≤ 50,000) more rocks appear, each at an integral distance Di from the start (0 < Di < L).
To play the game, each cow in turn starts at the starting rock and tries to reach the finish at the ending rock, jumping only from rock to rock. Of course, less agile cows never make it to the final rock, ending up instead in the river.
Farmer John is proud of his cows and watches this event each year. But as time goes by, he tires of watching the timid cows of the other farmers limp across the short distances between rocks placed too closely together. He plans to remove several rocks in order to increase the shortest distance a cow will have to jump to reach the end. He knows he cannot remove the starting and ending rocks, but he calculates that he has enough resources to remove up to M rocks (0 ≤ M ≤ N).
FJ wants to know exactly how much he can increase the shortest distance before he starts removing the rocks. Help Farmer John determine the greatest possible shortest distance a cow has to jump after removing the optimal set of M rocks.
Input
Line 1: Three space-separated integers: L, N, and M
Lines 2…N+1: Each line contains a single integer indicating how far some rock is away from the starting rock. No two rocks share the same position.
Output
Line 1: A single integer that is the maximum of the shortest distance a cow has to jump after removing M rocks
Sample Input
25 5 2
2
14
11
21
17
这道题的意思是说加上首尾的数组中去掉给定个数的数组元素,再去求其中任意两个元素的最小值的最大值。我们的思路是把距离当作二分查找的对象,再以此为标准带入题目给的数据中来看看是否满足条件。原理是当我们依次比较排序好的数组的相连两个元素时,如果他们的距离大于二分的值(也就是下文的mid),就统计个数并更新前坐标,否则就继续,例如比较第一个元素和第三个元素的距离,再重复操作。代码如下。
#include<iostream>
#include<math.h>
#include<algorithm>
#include<stdlib.h>
#include<stdio.h>
#include<string.h>
#define pi acos(-1.0)
using namespace std;
const int MAXN=1e5+10;
int a[MAXN];
long int l,n,m;
int search1(int l,int r) {
int k1,mid;
while(l<=r) { //
mid=(l+r)/2;//mid 是间距
k1=0;
int a1=0,a2=1;//找最小值并且考虑删减个数
while(a2<n) {
if(a[a2]-a[a1]>=mid) { //可以不删去
a1=a2;
a2++;
} else {
a2++;
k1++;
}
}
if(k1>m) { //mid太大了
r=mid-1;
} else l=mid+1;
}
return r;
}
int main() {
int l1,r;
cin>>l>>n>>m;
for(int i=1; i<=n; i++)
cin>>a[i];
a[0]=0;
a[n+1]=l;
sort(a,a+n+1);
r=l;
n++;
l1=0;
printf("%d",search1(l1,r));
}
再放道类题
The SUM problem can be formulated as follows: given four lists A, B, C, D of integer values, compute how many quadruplet (a, b, c, d ) ∈ A x B x C x D are such that a + b + c + d = 0 . In the following, we assume that all lists have the same size n .
Input
The first line of the input file contains the size of the lists n (this value can be as large as 4000). We then have n lines containing four integer values (with absolute value as large as 228 ) that belong respectively to A, B, C and D .
Output
For each input file, your program has to write the number quadruplets whose sum is zero.
Sample Input
6
-45 22 42 -16
-41 -27 56 30
-36 53 -37 77
-36 30 -75 -46
26 -38 -10 62
-32 -54 -6 45
Sample Output
5
Hint
Sample Explanation: Indeed, the sum of the five following quadruplets is zero: (-45, -27, 42, 30), (26, 30, -10, -46), (-32, 22, 56, -46),(-32, 30, -75, 77), (-32, -54, 56, 30).
这题的意思是依次读入四个数存入各自的集合,再从每个集合里面分别取出一个计算是否相加为零。这道题解决的关键就在于能否简化这四个数组从而继续进行我们接下去的二分查找。我们的解决方法是把a和b的数组看成二维数组,c、d数组也类似,从而进行两两元素相乘,化四个数组合并为两个数组,因此这两个合并数组的下标是n*n,接着进行二分查找。
要注意的是因为计算的零的数量,所以我们必须考虑每个集合存在相同元素的情况,因此必须要在每次二分查找是进行上下的额外检索。代码如下。
#include<iostream>
#include<math.h>
#include<algorithm>
#include<stdlib.h>
#include<stdio.h>
#include<string.h>
using namespace std;
const int MAXN=1e7+10;
double ans=0;
int a[MAXN],b[MAXN],c[MAXN],d[MAXN],tar1[3*MAXN],tar2[3*MAXN],i,j;
long int l,n,m;
int main() {
int k1=0,k2=0,cnt=0;
cin>>n;
for(i=0; i<n; i++) {
cin>>a[i]>>b[i]>>c[i]>>d[i];
}
for(i=0; i<n; i++) {
for(j=0; j<n; j++) {
tar1[k1++]=a[i]+b[j];
tar2[k2++]=c[i]+d[j];
}
}
sort(tar2,tar2+k2);
int mid;
for(int i=0;i<k2;i++)
{
int left=0;
int right=k2-1;
while(left<=right)
{
mid=(left+right)/2;
if(tar1[i]+tar2[mid]==0)
{
cnt++;
for(int j=mid+1;j<k2;j++)//前后查找
{
if(tar1[i]+tar2[j]!=0)
break;
else
cnt++;
}
for(int j=mid-1;j>=0;j--)
{
if(tar1[i]+tar2[j]!=0)
break;
else
cnt++;
}
break;
}
if(tar1[i]+tar2[mid]<0)
left=mid+1;
else
right=mid-1;
}
}
cout<<cnt;
}
以上就是入门二分查找的基本内容了。