题意:
数轴上有n个点 每个点重量1 可以移动其中k个到任何位置 使得题中式子值最小 di表示第i个点距离现在n个点的重心的距离
思路:
式子中wi可以去掉 因为都是1 则 式子变成I=min(sum(di*di))
考虑移动的k个点 应该直接把它们移到重心 这样di为0
很容易想到 我们将所有点排序后 应该从两边往中间拿 这样移动k个点 剩下一些连续的点 因此可以枚举剩下的连续区间
此时我们把I变形 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-k) )
这样我们可以用前缀和来维护答案 结合上面的枚举 复杂度也就是O(n)
代码:
#include<cstdio> #include<iostream> #include<cstring> #include<string> #include<algorithm> #include<map> #include<set> #include<vector> #include<queue> #include<cstdlib> #include<ctime> #include<cmath> #include<bitset> using namespace std; typedef long long LL; #define M 50010 #define mod 1000000007 int T, n, k; double s[M], s2[M]; double ans; int main() { int i, j; double tmp, mid; scanf("%d", &T); while (T--) { scanf("%d%d", &n, &k); for (i = 1; i <= n; i++) scanf("%lf", &s[i]); ans = 0; if (n != k) { sort(s + 1, s + n + 1); for (i = 1; i <= n; i++) s2[i] = s[i] * s[i]; double sum1 = 0, sum2 = 0; for (i = 1; i <= n - k; i++) { sum1 += s[i]; sum2 += s2[i]; } mid = sum1 / (n - k); ans = sum2 - 2.0 * mid * sum1 + mid * mid * (n - k); for (i = 2, j = n - k + 1; j <= n; i++, j++) { sum1 -= s[i - 1]; sum1 += s[j]; sum2 -= s2[i - 1]; sum2 += s2[j]; mid = sum1 / (n - k); tmp = sum2 - 2.0 * mid * sum1 + mid * mid * (n - k); if (tmp < ans) ans = tmp; } } printf("%.10f\n", ans); } return 0; }
原文地址:http://blog.csdn.net/houserabbit/article/details/40382247