Poj3579:中位数——题解

7 篇文章 0 订阅
5 篇文章 1 订阅

(题目请参考poj3579)
却说上回说到的魔法照相馆的主人是一位长者,他曾是过去陪同勇者作战的法师,而现如今的和平年代,他也不用上战场了,就在这里开了家照相馆,来接待过往的探险家们给他们提一些建议。
这天,长者特意邀请勇者过来尝试他最新的通灵魔法,大致的内容如下:
“……这是一个开发你自身潜力的魔法……我们通过感应器接收到来自你身体内部的灵气,并转化成数据,此时按照统计魔法来计算就会得到m=C(N,2)个答案,这个时候我们找到第(m+1)/2个小的数据,即是你需要开通灵气的地方,此时我们的魔法就成功了…………”
然而,面对勇者这样的单细胞生物来说,他的身上没有太多灵气,所以直接暴力枚举就能完成,不过这位长者想要为更多想成为魔法师的普通人开通灵气,因此所得到的数据很多,而且因为来照相馆的人很多,所以要快速解决。
很显然,这就需要用到路由器魔法师导论的精髓————二分魔法了。

(下列话语请依据程序(魔法)来看)

首先,我们完全可以立刻算出m进而算出我们要求的第几位数字——k,然后将初始数排序,这是我们最开始就应该做的。
然后,为了加快速度,所以我们采用二分的思想,但在我们写二分之前,我们介绍一个函数。
upper_bound(a+1,a+n+1,c)-a;
这个函数是用来计算从a【1】~a【n】之间的数字比c小的数的个数(而且很快)(咱们就不深究这个高深的魔法了)
然后我们改进一下ans+=t-i-1;(这里的ans是指有多少个中位数数是比mid小),那么t-i-1就是表示以第i的数为起点开始计算有多少个中位数数是比mid小的个数。

好的,我们来开始说一下二分的思想了。
对,二分答案。
我们知道答案的范围一定在0~a[n]-a[1](还记得咱们已经排过序了吗)
所以我们在这中间找答案,然后利用upper_bound找到比mid小的中位数的个数。
接下来就是咱们激动人心的特判环节了。
第一个特判没什么好说的,就是一个终止条件。
接下来我们看看最重要的两个特判。
如果我们求得的个数大于k的话,我们知道mid一定是过大了,于是suan(l,mid);
反之就是过小了,亦然。
那么我们来看一下困扰路由器多年的等号究竟应该放在哪里,是要单独拿出来特判吗,不行!(虽然不知道为什么但是过不了是肯定的——题外话),那么我们将这个并入一个特判里吧!
放在哪里呢?
我们不妨模拟下吧,我们假设得到了正确答案mid的话,那么这就意味着接下来所有的答案都是错误的,那么这也就是说;
当此时处于(l,mid)之间的时候,我们之后找到的所有的数都要比k小,那么将会一直执行最后一个特判,这样最终的得到的r就一定是正确答案;
反之的话,我们发现mid此时已经+1了,所以说永远也不可能得到正确答案了。
于是,我们决定,将等号放在了第二个特判表达式里

#include<cstdio>
#include<cstring>
#include<cmath>
#include<string>
#include<algorithm>
#include<iostream>
using namespace std;
int a[100002]={0};
int c,k;
int n;
void suan(int l,int r){
    if(l==r){
        printf("%d\n",l);
        return;
    }
    int ans=0;
    int mid=(l+r)/2;
    for(int i=1;i<=n;i++){
        int t=upper_bound(a+1,a+n+1,a[i]+mid)-a;
        ans+=t-i-1;
    }
    if(ans>=k)suan(l,mid);
    else if(ans<k)suan(mid+1,r);
}
int main(){
    while(cin>>n){
        memset(a,0,sizeof(a));
        for(int i=1;i<=n;i++){
            scanf("%d",&a[i]);
        }
        c=n*(n-1)/2;
        k=(c+1)/2;
        sort(a+1,a+n+1);
        suan(0,a[n]-a[1]);
    }
    return 0;
}
  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值