题目描述:
给定一个 N 个数的数组 cat[i],并用这个数组生成一个新数组 ans[i]。新数组定义为对于任意的 i, j 且 i != j,均有 ans[] = abs(cat[i] - cat[j]),1 <= i < j <= N。试求出这个新数组的中位数,中位数即为排序之后 (len+1)/2 位置对应的数字,’/’ 为下取整。
输入:
多组输入,每次输入一个 N,表示有 N 个数,之后输入一个长度为 N 的序列 cat, cat[i] <= 1e9 , 3 <= n <= 1e5 。
Sample Inpout:
4
1 3 2 4
3
1 10 2
输出:
输出新数组 ans 的中位数
Sample Output:
1
8
思路:
对于输入的cat序列,我们可以通过等差数列求和公式得知其经过处理后的数组大小,从而可以知道要求的中位数的排名,也就知道了小于中位数的数的个数。
我们将cat序列进行升序排序,则ans数组可由后一个数减去前一个数求得,从而可以去除绝对值,同时我们也可给出中位数的范围,即0到cat[n]-cat[1]。
因为答案具有单调性,我们可以考虑二分的做法,对中位数的范围进行二分,然后根据二分得到的数P,将其与中位数比较排名,若其大于中位数,则中位数在前半部分,继续二分前半部分,否则二分后半部分。
最后一个问题就是判断小于P的数有多少个,我们遍历i,求取j的范围,可得到小于P的数据个数记作sum,同时我们也求取等于P的数据个数记作equal。记中位数排名为m,若sum<m&&(equal+sum)>=m说明P就是中位数,若equal+sum<m说明P小于中位数,则继续二分中位数范围后半部分,若P大于中位数,继续二分前半部分,直到得到中位数。
代码:
#include <iostream>
#include <algorithm>
using namespace std;
int find(int *v,int x,int n,int i)
{
int l=i,r=n,ans=-1;
while(l<=r)
{
int mid=(l+r)>>1;
if(v[mid]<x)
{
ans=mid;
l=mid+1;
}
else
r=mid-1;
}
return ans;
}
int main()
{
int n;
while (scanf("%d",&n)!=EOF)
{
int *input = new int[n+1]();
for (int i = 1; i <= n; i++)
scanf("%d",&input[i]);
sort(input + 1, input + n + 1);
int l = 0, r = input[n] - input[1], mid = 0, m = ((n - 1)*n / 2 + 1) / 2, sum;
while (l <= r)
{
int equal=0;
sum=0;
mid = (l + r) >> 1;
for (int i = 1; i <= n; i++)
{
int temp= find(input,input[i]+mid,n,i);
if(temp!=-1)
{
for(int j=temp+1;j<=n&&input[j]==(input[i]+mid);j++)
equal++;
sum += temp-i;
}
else
for(int j=i+1;j<n&&input[j]==(input[i]+mid);j++)
equal++;
}
if(sum<m&&(equal+sum)>=m)
break;
else if(equal+sum<m)
l=mid+1;
else
r=mid-1;
}
cout << mid << endl;
}
return 0;
}