POJ3579 Median

  • 题意:给出N个数,对于存有每两个数的差值的序列求中位数(一共\(C_n^2\)个),如果这个序列有偶数个元素,就取中间偏小的作为中位数。

  • 思路:

注意到题目中对于每两个数求差值,所有数的排列顺序不影响结果,所以可以先对数组排序。

因为答案具有单调性,所以可以二分答案ans,check函数中求出差值小于等于ans的数对数量cnt,与总方案数(差值序列元素个数)的二分之一比较(中位数),更改ans的上下界。

这里可以枚举每个\(a_i\) ,统计小于等于\(a_i+ans\) 的数有多少个,计入cnt变量中。

具体方法:枚举 a[i], 然后二分 i之后的区间,假设a[j]是最后一个小于等于a[i]+ans的值,那么cnt加上j-i的值。

时间复杂度:O(\(nlog^2n\))

优化:依题意,数组中均为非负整数,可利用单调性,用i和j双指针进行优化。

时间复杂度:O(\(nlogn\))

  • 其他
    1. \(C_n^2=n*(n-1)/2\);
    2. check函数中注意原序列中元素个数为奇数还是偶数,若是偶数,依题意答案向下取,判断时不加等号。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn=100005,INF=0x3f3f3f3f;
int n,a[maxn];
bool check(int x){
    int cnt=0;
    for(int i=1,j=1;i<=n;++i){
        while(a[j]<=a[i]+x&&j<=n) ++j;
        --j;
        cnt+=j-i;
    }
    if(!(((n*(n-1))>>1)&1)) return cnt < (n*(n-1))>>2;
    else return cnt <= (n*(n-1))>>2;
}
int main(){
    while(scanf("%d",&n)!=EOF){
        for(int i=1;i<=n;++i) scanf("%d",&a[i]);
        sort(a+1,a+1+n);
        int l=0,r=INF;
        while(l<r){
            int mid=l+r>>1;
            if(check(mid)) l=mid+1;
            else r=mid;
        }
        printf("%d\n",l);
    }
    return 0;
} 

猜你喜欢

转载自www.cnblogs.com/yu-xing/p/10360672.html