题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=5073
解题报告:在一条直线上有n颗星星,一开始这n颗星星绕着重心转,现在我们可以把其中的任意k颗星星移动到这条直线上的任意位置然后围绕这个新的重心转, 这个星系的惯性值I = w1 * d1^2 + w2 * d2 ^ 2 +......+wn * dn ^ 2,其中wi表示第i颗星星的重量,di表示第i颗星星到这个星系的重心的距离.然后现在要你求转移k颗星星之后惯性值最小是多少?
n颗星星转移k颗后还剩下n-k颗是不能移动的,很显然转移的k颗星星一定是转移到重心的位置,然后每颗星星的重量都是1,所以求I时就可以不用管wi了,然后我们可以枚举n-k个星星所在的区间,而且可以确定这n-1个星星在位置上一定是连续的.然后枚举出了k个区间,怎么能在O(1)时间算出每个区间的I值呢?作如下转化(其中e表示重心的坐标):
(x1-e)^2 + (x2-e)^2 (x3-e)^2+....(xn-e)^2
=x1^2 + x2^2+...xn^2 + n * e^2 - 2*e*(x1+x2+x3+...xn)
这样通过预先求出x1的平方到xn的平方的和还有x1到xn的和就可以在O(1)时间内求出I的值了.
1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 #include<iostream> 5 #include<cmath> 6 using namespace std; 7 const int maxn = 50050; 8 const double eps = 1e-14; 9 10 int T,n,k; 11 double pos[maxn],sump[maxn],sump2[maxn]; 12 int main() 13 { 14 // freopen("in","r",stdin); 15 scanf("%d",&T); 16 while(T--) 17 { 18 scanf("%d%d",&n,&k); 19 for(int i = 1;i <= n;++i) 20 scanf("%lf",&pos[i]); 21 sort(pos+1,pos+n+1); 22 sump[0] = sump[1] = sump2[0] = sump2[1] = 0; 23 for(int i = 1;i <= n;++i) 24 { 25 sump[i] = sump[i-1] + pos[i]; 26 sump2[i] = sump2[i-1] + pos[i] * pos[i]; 27 } 28 int m = n - k; 29 if(m <= 1) 30 { 31 puts("0"); 32 continue; 33 } 34 double ans = sump2[m] + m * (sump[m] / m) * (sump[m] / m) - 2.0 * (sump[m] / m) * sump[m]; 35 for(int e = m;e <= n;++e) 36 { 37 int s = e - m + 1; 38 double eve = (sump[e] - sump[s-1]) / m; 39 double temp = sump2[e] - sump2[s-1] + m * eve * eve - 2.0 * eve * (sump[e] - sump[s-1]); 40 ans = min(ans,temp); 41 } 42 printf("%.12lf ",ans); 43 } 44 return 0; 45 }