题目:给出n个数字,求出一个子集,使得其平均数与中位数的差值最大。
解析:首先将a[1...n]从大到小排序,在固定中位数的位置后,子集的构成必然是在中位数位置的两侧选取相同个,显然,每侧要尽量选取较大的值。
可以发现,选取的个数与平均数具有凸函数性质,所以通过三分找到临界点。整体复杂度O(nlogn)。
[code]:
#include<bits/stdc++.h>
using namespace std;
const int maxn = 2e5+5;
int n,a[maxn];
int o1,o2;
double ans,s[maxn];
void init(){
ans = -1e9;
}
double f(int p,int x,int a,int b){
return (s[x-1]+s[p+x]-s[p]+b)/(2*x+a);
}
void sol(){
double median;
int i,j,p1,p2;
int mid1,mid2,l,r,t1,t2;
for(i = 0;i < 2*n-1;i++){
if(i&1) p1=i/2,p2=p1+1,t1=2,t2=a[p1]+a[p2],median=1.0*t2/2.0;
else p1=p2=i/2,t1=1,t2=a[p1],median=a[p1];
l = 0,r = min(p1,n-p2);
while(r-l>2){
mid1 = l+(r-l)/3;
mid2 = l+2*(r-l)/3;
if(f(p2,mid1,t1,t2)>=f(p2,mid2,t1,t2)) r = mid2;
else l = mid1;
}
for(j = l;j <= r;j++){
if(ans < f(p2,j,t1,t2)-median){
o1 = i,o2 = j;
ans = f(p2,j,t1,t2)-median;
}
}
}
printf("%d\n",2*o2+1+(o1&1));
for(i = 0;i < o2;i++) printf("%d ",a[i]);
printf("%d ",a[i=o1/2]);
if(o1&1) printf("%d ",a[i=o1/2+1]);
for(j = 0;j < o2;j++) printf("%d ",a[++i]);
}
int main(){
int i,j;
scanf("%d",&n);
for(i = 0;i < n;i++) scanf("%d",&a[i]);
sort(a,a+n);
reverse(a,a+n);
s[0] = a[0];
for(i = 1;i < n;i++) s[i] = s[i-1]+a[i];
sol();
return 0;
}