1. 程式人生 > >二分法-二分查詢的應用及三個經典例題

二分法-二分查詢的應用及三個經典例題

二分法-二分查詢應用及例題

在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 505
using 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;
}