1. 程式人生 > >簡單搜尋入門(二):二分答案 HDU 5248

簡單搜尋入門(二):二分答案 HDU 5248

二分練習的第二部分——二分答案的查詢

Description

給定序列A={A1,A2,…,An}, 要求改變序列A中的某些元素,形成一個嚴格單調的序列B(嚴格單調的定義為:Bi< Bi+1, 1≤i< N)。
我們定義從序列A到序列B變換的代價為cost(A,B)=max(|Ai−Bi|)(1≤i≤N)。請求出滿足條件的最小代價。注意,每個元素在變換前後都是整數。

Input

第一行為測試的組數T(1≤T≤10).
對於每一組:
第一行為序列A的長度N(1≤N≤105),第二行包含N個數,A1,A2,…,An.
序列A中的每個元素的值是正整數且不超過106。

Output

對於每一個測試樣例,輸出兩行:

第一行輸出:”Case #i:”。i代表第 i 組測試資料。

第二行輸出一個正整數,代表滿足條件的最小代價。

Sample Input

2
2
1 10
3
2 5 4

Sample Output

Case #1:
0
Case #2:
1

Solution

一開始閱讀時以為是一道貪心的題目,就用貪心的策略敲了一遍,發現一直WA。後來看了一眼資料範圍,就感覺像是二分,另一方面,注意到cost是最大值,只需要二分一下需要改變的最大值,然後判斷當前的答案能否將原來的數列改變成一個嚴格單調的序列。

Code

#include <cstdio>
#include <cstring>
#include <cstdlib>
#define MAXN 100003

int a[MAXN],n;
bool check(int s)
{
    int st=a[1]-s;//第一個數字越小對後來越有利
    for(int i=2;i<=n;i++)
    {
        int w=a[i]-s;//對當前的數字做最大尺度的改變
        if(w<=st)
        {
            w=st+1;//可以構造的話就讓當前的數字越小越好
if(abs(w-a[i])>s)//如果連最小的需求都無法滿足則返回 return false; } st=w; } return true; } int main() { int k; while(scanf("%d",&k)!=EOF) { for(int j=1;j<=k;j++) { printf("Case #%d:\n",j); scanf("%d",&n); for(int i=1;i<=n;i++) scanf("%d",&a[i]); int l=0,r=1000001;//初始化最大值 while(l<r) { int mid=(l+r)>>1; if(check(mid))//如果當前的值能夠成功取右區間 r=mid; else l=mid+1;//如果不能則取左區間 } printf("%d\n",r);//l與r都可以, } } return 0; }

Time Limit: 1000MS
Memory Limit: 65536K
Total Submissions: 8524
Accepted: 2973

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

Source

POJ Founder Monthly Contest – 2008.04.13, Lei Tao

題意

對於給定的N個數字,將N個數任意兩項的差值共M個全部算出,從小到大排序找出其中的中位數。

Solution

經典的二分查詢答案的題目,通過兩次的二分,第一次查詢答案,第二次二分判斷當前答案的正確性。
最大的差值不斷二分答案每次判斷一下當前差值t,用sum記錄比a[i]大t的數字的個數,如果sum大於m/2,就取右區間,反之取左區間。
PS:二分查詢在STL中有現成的模板
lower_bound(a,a+n,t)在陣列中查詢第一個大於(或等於)t的數字,返回指向大於(或等於)t的第一個迭代器—返回指標。
upper_bound(a,a+n,t)在陣列中查詢第一個大於t的數字,返回大於t的迭代器。

Code

#include <cstdio>
#include <algorithm>
using namespace std;

int n;
long a[100003],m,l,r,mid,res;
bool che (int t)
{
    int sum=0;
    for(int i=1;i<=n;i++)
        sum+=n-(lower_bound(a+1,a+n+1,a[i]+t)-a-1);
    if(sum>m)
        return true;
    else
        return false;
}
int main ()
{
    while(scanf("%d",&n)!=EOF)  
    {
        for(int i=1;i<=n;i++)
            scanf("%ld",&a[i]);
        sort(a+1,a+1+n);//排序保證二分的正確性
        m=n*(n-1)/4;
        l=0,r=a[n]-a[1];//初始化最大值為最大數字與最小數字的差值
        while(l<=r)//二分答案
        {
            mid=(l+r)>>1;
            if(che(mid))
            {
                res=mid;
                l=mid+1;
            }
            else
                r=mid-1;
        }
        printf("%d\n",res);
    }
    return 0;
}