二分法-二分查找的应用及三个经典例题

来源:互联网 发布:记考勤软件下载 编辑:程序博客网 时间:2024/06/15 11:33

二分法-二分查找应用及例题

在ICPC-ACM竞赛中,二分法是一种常用的解题策略,其中二分搜索是应用非常广泛的一种,主要使用的有STL中的binary_search()函数、lower_bound()函数、upper_bound()函数,这些函数一般要配合sort()、unique()函数使用。

1. binary_search(begin,end,index):在数组中,若找到index则返回1,找不到就返回0

2.lower_bound(begin,end,index):在数组中的[begin,end)前闭后开区间内,返回大于或等于index的第一个元素位置,如果都小于index,则返回end。

3.upper_bound(begin,end,index):在数组中的[begin,end)前闭后开区间内,返回大于index的第一个元素位置,如果都小于index,则返回end。

4.sort()和unique(),sort()用于对数组排序,一般在二分查找之前使用,unique(),用于对数组去重,某些情况下可用到。

例题

POJ - 2785 4 Values whose Sum is 0

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 2 28 ) 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,然后在a中查找b中的元素(若找到则相加等于0),然后使用upper_bound(sum1,sum1+m,k)-lower_bound(sum1,sum1+m,k)
即得到重复和数对的个数解,所有解相加即得答案。
答案:

//二分法+stl#include<cstdio>#include<iostream>#include<algorithm>using namespace std;const int maxn=4020;int a[maxn],b[maxn],c[maxn],d[maxn],sum1[maxn*maxn];int main(){ int n;  while(~scanf("%d",&n)){ int m=0;  for(int i=0;i<n;i++)      cin>>a[i]>>b[i]>>c[i]>>d[i];  for(int j=0;j<n;j++)  for(int i=0;i<n;i++)      sum1[m++]=a[j]+b[i];  sort(sum1,sum1+m);  long long ans=0;  for(int i=0;i<n;i++)    for(int j=0;j<n;j++)      {        int k=-(c[i]+d[j]);        ans+=upper_bound(sum1,sum1+m,k)-lower_bound(sum1,sum1+m,k);      }cout<<ans<<endl;}}

HDU - 2141 Can you find it?

Give you three sequences of numbers A, B, C, then we give you a number X. Now you need to calculate if you can find the three numbers Ai, Bj, Ck, which satisfy the formula Ai+Bj+Ck = X.
Input

There are many cases. Every data case is described as followed: In the first line there are three integers L, N, M, in the second line there are L integers represent the sequence A, in the third line there are N integers represent the sequences B, in the forth line there are M integers represent the sequence C. In the fifth line there is an integer S represents there are S integers X to be calculated. 1<=L, N, M<=500, 1<=S<=1000. all the integers are 32-integers.
Output

For each case, firstly you have to print the case number as the form “Case d:”, then for the S queries, you calculate if the formula can be satisfied or not. If satisfied, you print “YES”, otherwise print “NO”.
Sample Input

3 3 3
1 2 3
1 2 3
1 2 3
3
1
4
10

Sample Output

Case 1:
NO
YES
NO

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).
此题笔者疯狂MLE了十多次,此题要求的Memory limit 为10000 kB ,Time limit为3000 ms 显然不能用三个for,同样分为2个组

#include<bits/stdc++.h>#define maxn 505using namespace std;int L[maxn],M[maxn],N[maxn],sum[maxn*maxn];int main(){ //int num=0;  int l,m,n,s,s2;  int kase=1;  while(scanf("%d%d%d",&l,&m,&n)!=EOF)  {    for(int i=0;i<l;i++)      scanf("%d",&L[i] );    for(int j=0;j<m;j++)      scanf("%d",&M[j] );    for(int k=0;k<n;k++)      scanf("%d",&N[k]);    int num=0;    for(int i=0;i<l;i++)        for(int j=0;j<m;j++)          sum[num++]=L[i]+M[j];  sort(sum,sum+num);  scanf("%d",&s );  printf("Case %d:\n",kase++);while(s--)  {scanf("%d",&s2 );int flag=0;for(int i=0;i<n;i++){  if(binary_search(sum,sum+num,s2-N[i]))  {flag=1;    break;  }}if(flag)    puts("YES");else    puts("NO");  }  }}

POJ - 3579 Median

Given N numbers, X1, X2, … , XN, let us calculate the difference of every pair of numbers: ∣Xi - Xj∣ (1 ≤ i < j ≤ N). We can get C(N,2) differences through this work, and now your task is to find the median of the differences as quickly as you can!

Note in this problem, the median is defined as the (m/2)-th smallest number if m,the amount of the differences, is even. For example, you have to find the third smallest one in the case of m = 6.
Input

The input consists of several test cases.
In each test case, N will be given in the first line. Then N numbers are given, representing X1, X2, … , XN, ( Xi ≤ 1,000,000,000 3 ≤ N ≤ 1,00,000 )
Output

For each test case, output the median in a separate line.
Sample Input

4
1 3 2 4
3
1 10 2

Sample Output

1
8

这题是利用二分法枚举答案,用二分查找判断是否是中位数,个人感觉比较难理解判断代码

#include<iostream>#include<cstdio>#include<algorithm>using namespace std;const int N = 100005;int a[N], n, m;bool judge(int x) {    int cnt = 0;    for (int i = 0; i < n; i++)        cnt += n - (lower_bound(a, a + n, a[i] + x) - a);    return cnt > m ? true : false;//如果这个数恰好是}int main() {    while (~scanf("%d", &n)) {        m = n * (n - 1) / 4;        for (int i = 0; i < n; i++)            scanf("%d", &a[i]);        sort(a, a + n);        int l = 0, r = a[n - 1];        while (l <= r) {            int mid = (l + r) / 2;            if (judge(mid))                l = mid + 1;            else                r = mid - 1;        }        printf("%d\n", l - 1);    }    return 0;}