问题:
TT 是一位重度爱猫人士,每日沉溺于 B 站上的猫咪频道。
有一天,TT 的好友 ZJM 决定交给 TT 一个难题,如果 TT 能够解决这个难题,ZJM 就会买一只可爱猫咪送给 TT。
任务内容是,给定一个 N 个数的数组 cat[i],并用这个数组生成一个新数组 ans[i]。新数组定义为对于任意的 i, j 且 i != j,均有 ans[] = abs(cat[i] - cat[j]),1 <= i < j <= N。试求出这个新数组的中位数,中位数即为排序之后 (len+1)/2 位置对应的数字,'/' 为下取整。
TT 非常想得到那只可爱的猫咪,你能帮帮他吗?o
input:
多组输入,每次输入一个 N,表示有 N 个数,之后输入一个长度为 N 的序列 cat, cat[i] <= 1e9 , 3 <= n <= 1e5
output:
输出新数组 ans 的中位数
sample input:
4
1 3 2 4
3
1 10 2
sample output:
1
8
题解:
-
对于我们这个题来说,我们的目的是求cat[i]-cat[j]的中位数,我们首先来想,怎样求中位数。我们求中位数就是按照名次来,而对于这个我们是不是可以用二分法来比较一个数的名次是不是等于我们这个中位数的名次。
-
根据上面的分析,我们来看,二分时,一个数的名次如果大于中位数的名次,那么就去比他小的一半来,反之亦然。
-
那么我们如何来计算每一个结果的名次呢,因为名次意味这有多少个结果比自己小,那么我们就可以先对cat进行排序,就可以将绝对值去除,去除之后,我们就去寻找有多少结果比该结果小。对于每一个i,我们去判断j的个数,使catj-cati小于该结果,就是这个结果的名次。
-
因此,我们可以使用两个二分对于我们的这个实验进行解决。
完整代码:
#include <iostream>
#include<stdio.h>
#include<stdlib.h>
#include <algorithm>
#include <cstring>
#include <cmath>
using namespace std;
int cat[100010];
/* run this program using the console pauser or add your own getch, system("pause") or input loop */
int find(int *cat,int size,int a) //找到一个数在有序数列中的位次
{
int l=0;
int r=size-1;
int count=-1;
int mid;
while(l<=r)
{
mid=(l+r)>>1;
if(cat[mid]<=a)
{
count=mid;
l=mid+1;
}
else if(cat[mid]>a)
{
r=mid-1;
}
}
return count;
}
int main(int argc, char** argv) {
int n;
while(scanf("%d",&n)!=EOF)
{
for(int i=0;i<n;i++)
{
scanf("%d",cat+i);
}
sort(cat,cat+n);
int realmid=((n*(n-1)/2)+1)/2;
int mid=0;
int l=0;
int r=cat[n-1]-cat[0];
int ans;
//...................//二分找中位数
while(l<=r)
{
int rank=0;
mid=(l+r)>>1;
for(int i=0;i<n;i++)
{
int a=find(cat,n,cat[i]+mid);
if(a!=-1)
{
rank+=(a-i);
}
}
if(rank>=realmid)
{
ans=mid;
r=mid-1;
}
else
{
l=mid+1;
}
}
cout<<ans<<endl;
}
return 0;
}
反思:
对于这个实验,其实就是我们对于二分发的应用,但是在解题思路上是不容易想到的,所以我们要多刷题。