题目链接http://acm.hdu.edu.cn/showproblem.php?pid=5073
题目可以自己看链接。
做这场比赛的时候没考虑清楚,M个全拿是考虑到了。但是没有考虑到数学公式的分解。看了一下几个大神的博客。发现自己数学还是很渣渣的。膜拜神牛Orz。
题意:
数轴上有n个点 每个点重量1 可以移动其中m个到任何位置 使得题中式子值最小 di表示第i个点距离现在n个点的重心的距离
思路:
I=min(sum(di*di))
必须把m个点移动到中心才有最小值
直接枚举最小区间
I = min(sum(di*di))
= min(sum((li-mi)*(li-mi))) (li即i点的位置 mi为这几个点的重心)
= min( sum(li*li) - 2*mi*sum(li) + mi*mi*(n-m) )
代码如下:
#include <iostream>
#include <cstdio>
#include <climits>
#include <cstring>
#include <cstdlib>
#include <cmath>
#include <vector>
#include <queue>
#include <algorithm>
#define esp 1e-6
#define inf 0x0f0f0f0f
#define LL long long
using namespace std;
double s[50010],s1[50010];
double sum1,sum2;
int main()
{
int T,i,j,k,n,m;
double ans,mid,temp;
scanf("%d",&T);
while(T--)
{
ans=0;
scanf("%d%d",&n,&m);
for(i=1;i<=n;i++)
{
scanf("%lf",&s[i]);
}
if(n!=m)
{
sum1=sum2=0;
sort(s+1,s+n+1);
for(i=1;i<=n;i++) s1[i]=s[i]*s[i];
for(i=1;i<=n-m;i++)
{
sum1+=s[i];
sum2+=s1[i];
}
mid=sum1/(n-m);
ans=sum2-2.0*mid*sum1+mid*mid*(n-m);
for(i=2,j=n-m+1;j<=n;i++,j++)
{
sum1-=s[i-1];
sum1+=s[j];
sum2-=s1[i-1];
sum2+=s1[j];
mid=sum1/(n-m);
temp=sum2-2.0*mid*sum1+mid*mid*(n-m);
if(temp<ans)
{
ans=temp;
}
}
}
printf("%.10f\n",ans);
}
}