C-TT的神秘礼物

题目:

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 非常想得到那只可爱的猫咪,你能帮帮他吗?

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

题目分析:

  • 暴力做法,枚举i, j将数列ans计算出来,然后取它的中位数 时间复杂度和空间复杂度均为𝑂(𝑛2),无法接受。

  • 所以先分析一波, 因为ans[] = abs(cat[i] - cat[j]),1 <= i < j <= N。,所以首先把绝对值去了,即j>i且cat[j] > cat[i]时ans[ ]=cat[j] -cat[i];所以在这里,先对cat数组进行从小到大的排序,就能去掉绝对值。

    •要求数列ans的中位数,也就是计算每一个p=ans[k]=cat[j] -cat[i]的名次,然后根据名次进行二分,当p的名次比实际中位数的名次小时,说明p小于中位数,l=mid+1;当p的名次不比实际中位数的名次小时,说明p大于等于中位数,r=mid-1;

    •(个人觉得这种方法最难的在于理解并计算p的名次)因为p=ans[k]=cat[j] -cat[i],所以p的名次也就是枚举下标i然后计算满足条件的下标j的个数。for循环遍历下标i,利用再一次的二分法构建函数find(int x,int n,int *a)找到数组a中a[j]<=[i]+p的最后一个位置,并返回,那么该p值的名次即为j-i !!!(因为还有一个基本条件j>i要满足)

(注意计算完名次后找中位数的二分的while循环条件为while(l<=r),之前写成了while(l<r)结果wrong answer了qaq)

代码:

#include<iostream>
#include<stdio.h>
#include<algorithm> 
using namespace std;

int find(int x,int n,int *a)//找到数组a中j<=i+p的最后个位置 
{
	int l=0;int r=n-1;int ans=-1;
	while(l<=r)
	{
		int mid=(l+r)>>1;
		if(a[mid]<=x)
		{
			ans=mid;
			l=mid+1;		
		}
		else r=mid-1;
	}
	return ans;	
}
int main()
{
	int N; 
	while(scanf("%d",&N)!=EOF)
	{
		int *cat=new int[N];
		for(int i=0;i<N;i++)
		{
			scanf("%d",&cat[i]);
		}
		sort(cat,cat+N);

		int len=N*(N-1)/2;//中位数应该在的位置
		int real_mid=(len+1)/2;
		int l=0;
		int r=cat[N-1]-cat[0]; 
		int re;
		while(l<=r)
		{
			int num=0;//计算每一个p>=xj-xi的j的个数,即为名次 
			int mid=(l+r)/2;
			for(int i=0;i<N;i++)
			{
				int ans=find(cat[i]+mid,N,cat);
				if(ans!=-1)//存在于并且找到了在数组中的最后一个位置
				{
					num+=(ans-i);
				}	
   			}
			if(num>=real_mid)
			{
				re=mid;
				r=mid-1;
			}
			else
			{
				l=mid+1;
			}
		}
		printf("%d\n",re); 
	}
	return 0;
} 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值