[HDU 3480]Division

Description

题库链接

给你一个大小为 $n$ 的集合,将其划分为若干子集。要求所有子集取并集为全集。每一个子集的贡献为该集合内最大元素与最小元素差的平方。求所有划分中最少的贡献和。

$1\leq n\leq 10000,1\leq m\leq 5000$

Solution

将集合内元素从大到小排序。由贪心的思想,这一问题转换为将这一序列划分为 $m$ 段,最小化贡献和。

可以 DP,$f_{i,j}$ 为前 $i$ 个元素划分为 $j$ 段的最小贡献。这一 DP 同[HDU 2829]Lawrence

Code

#include <bits/stdc++.h>
#define w(i, j) ((a[j]-a[i])*(a[j]-a[i]))
using namespace std;
const int N = 5000+5;

int f[N<<1][N], n, m, a[N<<1], s[N<<1][N], id;

void work() {
    scanf("%d%d", &n, &m); ++id;
    for (int i = 1; i <= n; i++) scanf("%d", &a[i]);
    sort(a+1, a+n+1);
    for (int i = 1; i <= n; i++)
        f[i][1] = (a[i]-a[1])*(a[i]-a[1]), s[i][1] = 1;
    for (int i = 1; i <= m; i++) s[n+1][i] = n;
    for (int j = 2; j <= m; j++)
        for (int i = n; i >= 1; i--) {
            f[i][j] = ~0u>>1;
            for (int k = s[i][j-1]; k <= s[i+1][j]; k++)
                if (f[i][j] > f[k-1][j-1]+w(k, i)) {
                    f[i][j] = f[k-1][j-1]+w(k, i);
                    s[i][j] = k;
                }
        }
    printf("Case %d: %d\n", id, f[n][m]);
}
int main() {
    int t; scanf("%d", &t);
    while (t--) work();
    return 0;
}
posted @ 2020-08-06 11:08  NaVi_Awson  阅读(66)  评论(0编辑  收藏  举报