题意:给定一个 N 个数的数组 cat[i],并用这个数组生成一个新数组 ans[i]。新数组定义为对于任意的 i, j 且 i != j,均有 ans[] = abs(cat[i] - cat[j]),1 <= i < j <= N。试求出这个新数组的中位数,中位数即为排序之后 (len+1)/2 位置对应的数字,’/’ 为下取整。cat[i] <= 1e9 , 3 <= n <= 1e5
思路:因为如果求出新数组进行排序找中位数,复杂度为o(n^2),显然在1s内不能运行。所以这时可以运用二分答案,对答案进行二分,对于每个答案,都求出一个名次(<=该数的个数),名次大于等于(len+1)/2的fist即是中位数
总结:单调的都可以进行二分!重要的是可以看出中位数对应的名次,这是单调的,所以想到二分答案!
反思:卡常,scanf比cin输入的时间快
代码:
#include<iostream>
#include<algorithm>
#include<cstdio>
using namespace std;
int a[100002];
int main()
{
int n;
while(scanf("%d",&n)!=EOF)
{ for(int i=1;i<=n;i++)
scanf("%d",&a[i]);//注意:scanf比cin快!!!
sort(a+1,a+n+1);
int kk=(n*(n-1)/2+1)/2;
int l=0;int r=1000000000;int flag=-1;
while(l<=r)//二分中位数
{
int mid=(l+r)/2;int count=0;
for(int i=1;i<=n;i++)
{
int num=mid+a[i];
int ll=i+1;int rr=n;int pos=-1;
while(ll<=rr)
{
int mmid=(ll+rr)/2;
if(a[mmid]<=num)
{
pos=mmid;ll=mmid+1;
}else rr=mmid-1;
}
if(pos!=-1) count=count+pos-(i+1)+1;
}
if(count>=kk) //找到名次>=(len+1)/2 不要只找等于 因为可能会有重复的值,都等于中位数,这时,名次会高
{ //找第一个first即可
flag=mid;r=mid-1;
}
else l=mid+1;
} cout<<flag<<endl;
} return 0;
}