POJ 3579 Median 查找中间值 二分

来源:互联网 发布:广州云计算培训 编辑:程序博客网 时间:2024/04/28 02:49

http://poj.org/problem?id=3579

这个题求的是一列数所有相互之间差值的序列的最中间的值是多少。看到数据可以看到用普通的n^2方法必定超时。

下面给出我写的两种方法。

第一种方法是用的是upper_bound,功能是在一段单调递增的序列中找到第一个大于目标元素的地址。用处是可以统计小于或等于value的元素有多少个(所以要减1)。该函数使用二分快速查找,时间性能log n。思路是对于某个确定的d,通过枚举第一个元素的下标来统计以这个元素为首的数对有多少个是满足a[j] - a[i] <= d 的,然后累加,从而计算出有多少个数对的差值是小于等于d的。记差值小于等于d的数对的个数关于d的函数为cnt(d),该函数单调递减,故使用左开右闭区间进行二分查找。

第二种方法使用的是lower_bound,功能是在一段单调递增的序列中找到第一个大于或等于目标元素的地址。用处是可以统计小于value的元素有多少个。使用左闭右开区间。


#include <cstdio>#include <iostream>#include <cstring>#include <algorithm>using namespace std;int a[100000+5];int n, m;bool ok(int d){    int cnt = 0;    for(int i=0; i<n; i++){        cnt += (upper_bound(a+i, a+n, a[i]+d)-1 - (a+i));    }    if(cnt >= m) return 1;    else return 0;}int main(){    while(scanf("%d", &n) != EOF){        int c = n*(n-1)/2;    if(c%2 == 0) m = c/2;    else m = c/2 + 1;        for(int i=0; i<n; i++)            scanf("%d", &a[i]);        sort(a, a+n);        int lb = -1, ub = a[n-1] - a[0];        while(ub - lb > 1){            int mid = (lb+ub)>>1;            if(ok(mid)) ub = mid;            else lb = mid;        }        printf("%d\n", ub);    }    return 0;}

/*AC*/#include <cstdio>#include <cstring>#include <iostream>#include <algorithm>using namespace std;const int maxn = 1e5+5;int a[maxn];int n, m;bool test(int val){    int cnt = 0;    for(int i=0;i<n;i++){        cnt += (n-1)-(lower_bound(a+i+1,a+n,a[i]+val)-a)+1;    }    if(cnt > m) return 1;    else return 0;}int main(){    while(scanf("%d",&n)!=EOF){m = n*(n-1)/4;for(int i=0;i<n;i++)    scanf("%d",&a[i]);sort(a,a+n);    int lb = 0, ub = a[n-1] - a[0] + 1;    while(ub - lb > 1){        int mid = (lb+ub)>>1;        if(test(mid)) lb = mid;        else ub = mid;        }    printf("%d\n", lb);    }    return 0;}



0 0
原创粉丝点击