Description
给你一个大小为 (n) 的集合,将其划分为若干子集。要求所有子集取并集为全集。每一个子集的贡献为该集合内最大元素与最小元素差的平方。求所有划分中最少的贡献和。
(1leq nleq 10000,1leq mleq 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
", id, f[n][m]);
}
int main() {
int t; scanf("%d", &t);
while (t--) work();
return 0;
}