首页 > 代码库 > HDU 5073 Galaxy

HDU 5073 Galaxy

题意:

数轴上有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;
}


HDU 5073 Galaxy