Robert 的军队

来源:互联网 发布:深圳盘古数据上市了没 编辑:程序博客网 时间:2024/05/17 02:11

Description

给出n个数,你可以从中选出x个数,l<=x<=r,使得这x个数的方差最小。求这个方差。
n<=10^5

Solution

看到这道题,首先是懵逼,方差最小?波动最小?
那么肯定是排序之后选连续的一段。
既然是连续的一段,那么我们就可以O(1)算出方差。

S2=i=1n(a[i]x¯)2

然后化简之后就是
S2=i=1na[i]2(ni=1a[i])2n

于是区间[i,j]的方差就是
S2=sqr[j]sqr[i1]ji+1(sum[j]sum[i1]ji+1)2

然后,你可以打一个表,或者感性的理解一下,这样选择的话,选多的一定不比选少的优。
于是我们只用选l个数。(感性理解一下)
O(n)扫一遍就好了。

Code

#include<cstdio>#include<cstring>#include<algorithm>#define fo(i,a,b) for(int i=a;i<=b;i++)#define N 100005using namespace std;typedef double db;typedef long long ll;db sqr[N],sum[N],ans;int a[N],n,l,r;int main() {    freopen("army.in","r",stdin);    freopen("army.out","w",stdout);    scanf("%d%d%d",&n,&l,&r);    fo(i,1,n) scanf("%d",&a[i]);    sort(a+1,a+n+1);ans=1000000000000000000;    fo(i,1,n) sum[i]=sum[i-1]+a[i];    fo(i,1,n) sqr[i]=sqr[i-1]+(ll)a[i]*a[i];    if (n<=2000) {        fo(i,l,r)             fo(j,i,n) {                db s=(sum[j]-sum[j-i])*1.0/i;                s*=s;s=(sqr[j]-sqr[j-i])*1.0/i-s;                ans=min(ans,s);            }        printf("%.3lf",ans);        return 0;    }      fo(i,l,n) {        db s=(sum[i]-sum[i-l])*1.0/l;        s*=s;s=(sqr[i]-sqr[i-l])*1.0/l-s;        ans=min(ans,s);    }    printf("%.3lf",ans);}
0 0
原创粉丝点击