poj3579 二分搜索+二分查找

Description

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 = 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

分析:

 

題目大意:N個數字,任意兩個數字之間的差共有m=(n-1)*n/2種,然後在輸出這些數字中間的哪一個。規則爲:若m爲奇數,則中間的那一個爲第(m-1)/2小的數字,若m爲偶數,則中間的數字指的是第m/2小的數字。

思路:

1)先將a[n]排序。
2)x 爲中位數 , |aj−ai|<x 的個數恰好爲一半, 所有的aj>ai 中 aj<x+ai 個數爲一半 。
     x 從 [0,max{|aj−ai|}] 中二分查找
     枚舉i,在a[n]中利用uppperBound()二分查找x+ai<aj(aj>ai) 的個數,若恰爲一半,則x爲所求
 

代碼如下:

public class BinarySearchTest {


  private static int n = 0;
  private static int m = 0;
  private static int[] array;
  private static int mid = 0;
  public static void main(String[] args) {
    Scanner scanner =  new Scanner(System.in);
    System.out.println("請輸入數字個數:");
    n = scanner.nextInt();
    scanner.nextLine();
    System.out.println("請輸入數字串(不同數字鍵空格間隔): ");
    String[] strs = scanner.nextLine().trim().split(" ");
    array = new int[n];
    for (int i = 0; i < n; i++){
      array[i] = Integer.parseInt(strs[i]);
    }
    scanner.close();
    m = n*(n-1) >> 1;
    mid = (m+1) >> 1;
    Arrays.sort(array);
    int l = 0;
    int r = array[n-1] - array[0];
    int ans = 0;
    while (l <= r){
      int middle = (l + r) >> 1;
      if (check(middle)){
        ans = middle;
        r = middle -1;
      } else {
        l = middle + 1;
      }
    }
    System.out.println(ans);
  }

  //統計比a[i]+d小的數有多少個,如果 >=(m+1)/2就return true else return false
  public static boolean check(int x){
    int cnt = 0;
    for (int i = 0; i < n; i++){
      //比a[i]+x小的元素的個數
      int t = upperBound(array, 0, n, array[i] + x);
      //排除a[i]之前的那些元素,因爲i從0開始,所以共有i+1個
      cnt += (t - i - 1);
    }
    //比a[i]+x大的元素個數若大於中位數,則返回true,同時也說明r應該縮小
    if (cnt >= mid){
      return true;
    }
    return false;
  }

  //在按照非遞減順序排好序的數組中找到第一個大於給定值target的那個數的座標
  public static int upperBound(int[] nums, int l, int r, int target) {
    while (l < r) {
      int m = (l + r) >> 1;
      if (nums[m] <= target) {
        l = m + 1;
      } else {
        r = m;
      }
    }
    return l;

  }

}

題目來源:http://poj.org/problem?id=3579

參考:https://www.cnblogs.com/xuejianye/p/5525747.html

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章