信息競賽學習筆記: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愉快!
發佈了30 篇原創文章 · 獲贊 3 · 訪問量 2萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章