Week4 C TT 的神秘礼物

题目描述:

给定一个 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; 
}
发布了32 篇原创文章 · 获赞 0 · 访问量 687

猜你喜欢

转载自blog.csdn.net/qq_43814559/article/details/104979036