SDU程序设计思维与实践 week4 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 非常想得到那只可爱的猫咪,你能帮帮他吗?

输入描述

多组输入,每次输入一个 N,表示有 N 个数,之后输入一个长度为 N 的序列 cat, cat[i] <= 1e9 , 3 <= n <= 1e5

输出描述

输出新数组 ans 的中位数

输入样例

4
1 3 2 4
3
1 10 2

输出样例

1
8

思路分析

1、通过暴力枚举计算数列ans[]的时间复杂度为O( N 2 N^2 N2),无法接受
2、本题通过二分答案解决。

  • 将cat[]数组进行从小到达排序以去绝对值号。
  • 设数列ans[]的中位数为P
  • 利用二分法求解P。设P的左界为0,右界为cat.back()-cat.front()。
  • 对每一个Pmid,求解Pmid在数组ans中的位次
  • 如果位次比中位数所在的位次小,那么pl=pmid+1;如果位次大于等于中位数的位次,那么pr=pmid-1。此步骤的目的是为了保证最后的答案是由原cat[]数组中的两个元素做差产生的。最后的pmid将会是位次等于中位数位次的最小的值。

3、计算Pmid在ans数组中位次的方法:

  • 计算满足条件 X i − X j ≤ P ( i < j , X i , X j ∈ c a t [ ] ) X_ i-X_j\leq P (i<j,X_i,X_j \in cat[]) XiXjPi<j,Xi,Xjcat[] 的(i,j)元素对的个数
  • 移项得 X j ≤ P + X i X_ j \leq P+ X_i XjP+Xi.循环枚举i,每步计算满足条件的j的个数。
  • 下标j的计算方法:在cat数组中二分查找。记录下 X j ≤ P + X i X_ j \leq P+ X_i XjP+Xi X j X_j Xj在cat数组中最靠右的(最大的)位置pos。那么cat数组中从i+1到pos位置的所有元素都是符合题意的,即j有pos-i个可行的取值。
  • 将遍历过程中的满足条件的j的个数加起来即得到当前P在ans数组中的位次。

4、输入数据请用scanf,用cin会超时

代码

#include<iostream>
#include<algorithm>
#include<vector>
#include<cstdio>
using namespace std;
int n;
vector<int> cat;

//找到<=x的最后一个位置 
int findr(int x){
	int l=0,r=cat.size()-1,ans=-1;
	while(l<=r){
		int mid=(l+r)/2;
		if(cat[mid]<=x){
			ans=mid;
			l=mid+1;
		}
		else r=mid-1; 
	} 
	return ans;
}
int main(){
	while(scanf("%d",&n)!=EOF){
		cat.clear();
		for(int i=0;i<n;i++){
			int temp=0;scanf("%d",&temp);
			cat.push_back(temp);
		}	
		//从小到大排列去绝对值 
		sort(cat.begin(),cat.end());
		//n个元素,数列x中元素的个数为n(n-1)/2 ,位数排第【(n(n-1)+2)/4】 
		int targetrank=(n*(n-1)+2)/4;
	//	cout<<"目标名次是"<<targetrank<<endl;	 
		//p的最大值、最小值 
		int pr=cat.back()-cat.front(),pl=0,ans=-1;	
		while(pl<=pr){
			int pmid=(pl+pr)/2,ppos=0;
			//固定下标i然后计算满足条件的下标j的个数(即为p的名次) 
			for(int i=0;i<cat.size();i++){
				int b=cat[i]+pmid;
				int pos=findr(b);
				if(pos!=-1){
					ppos+=pos-i;
				}
			}		
			if(ppos>=targetrank){
				ans=pmid;
				pr=pmid-1;
			}			
			else {
				pl=pmid+1;
			}	
	   	}
		   cout<<ans<<endl;	
	}
		
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值