信息竞赛学习笔记:POJ3579中位数(二分)

Median
Time Limit: 1000MS Memory Limit: 65536K
Total Submissions: 5066 Accepted: 1618

Description

Given N numbers, X1X2, ... , XN, let us calculate the difference of every pair of numbers: ∣Xi - Xj∣ (1 ≤ i  j  N). We can get C(N,2) differences through this work, and now your task is to find the median of the differences as quickly as you can!

Note in this problem, the median is defined as the (m/2)-th  smallest number if m,the amount of the differences, is even. For example, you have to find the third smallest one in the case of = 6.

Input

The input consists of several test cases.
In each test case, N will be given in the first line. Then N numbers are given, representing X1X2, ... , XN, ( X≤ 1,000,000,000  3 ≤ N ≤ 1,00,000 )

Output

For each test case, output the median in a separate line.

Sample Input

4
1 3 2 4
3
1 10 2

Sample Output

1
8

Source

      题的大概意思是给你N个数,两两做差取绝对值,再找出这些绝对值的中位数。老师的思路一开始是在求完差的快排中再次二分,然而我并没有学会,且这个N<100000,光求差就炸了,何况一个测试点还多组数据………老师之后提示我双重二分,在参考了一些思路后,我写出了一种二分。代码如下:
#include<iostream>
#include<string>
#include<algorithm>
#include<cstdio>
using namespace std;
const int maxn=110000;
int n,a[maxn],k,lp,rp,mid;
bool check(int mid)
{
	int sum=0,temp;
	for(int i=0;i<n;i++)
		{
			temp=a[i]-(mid-1);
			sum+=&a[i]-lower_bound(a,a+n,temp);
		}
	if(sum>=k) return false;
	else return true;
}
int main()
{
	while(scanf("%d",&n)!=EOF)
	{
		for(int i=0;i<n;i++)
		   scanf("%d",&a[i]);
		k=n*(n-1)/2;
		if(k%2==0) k=k/2;
		else k=k/2+1;
		sort(a,a+n);
		lp=0;rp=a[n-1]-a[0];
		while(rp-lp>1) 
		{
			mid=(lp+rp)/2;
			if(check(mid)) lp=mid;
			else rp=mid;
		}
		cout<<lp<<endl;
	}
	return 0;
}
这道题有几个坑点:
第一个:读数不能用cin,否则必超无疑!(我的AC啊 抓狂
第二个:在判断完check()后,选择的是lp=mid而不是lp=mid+1,所以,循环跳出条件是rp-lp>1,而不是lp<rp因为一旦rp-lp==1且check()成立,那么就会无限循环lp=mid,而(lp+rp)/2==mid==lp,输出lp就可以了。
第三个:探寻中位数位置k的时候要分奇偶。
第四个:活用lower_bound(),来访求比a[i]小mid-1的数的位置(这样就可以确定这些差值小于mid的个数,一旦大于等于k那么mid就取大了,往小了缩(rp=mid),否则往大了放(lp=mid)。
第五个:一个测试点好几组数据,加一块算时间不能超一秒(话说为什么好多二分的其他题都是2s,甚至还有8s的,然而感觉时间复杂度没这个高,数据也没这个大 疑问)。
第六个:while(scanf("%d".&n)!=EOF)别忘在主函数的最外面写。
第七个:上传之前千万别忘记删freopen(如果大家也有用freopen写小数据在本地测试的习惯,讨厌小黑框复制粘贴或手敲)。
祝OIER们Coding愉快!
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值