题意很好理解
题解 : 首先我们观察到几个事实就是说 :
1. 这 n 个数可以分成 k 段 并且这 k 段互不影响
2. 每个段内的数都是连续的数,因为只有这样,才能保证这个问题的最小性
3. 如果这 n 个数排序后每连续的两个作差取绝对值再求和答案是固定的也就是 max (a[i]) - min (a[i]) 。
4. 如果第三个是确定的并且可以保证每个段内的数都是连续的数,那我们所关心的最小值肯定就在于分段的时候的断点处的地方的两个数之差的最大值,最后用 max (a[i]) - min (a[i]) - dp[num1][num2] 就是答案了。
其中 dp[i][j] 表示长度为 len + 1 和 len 的段已经分了 i 段 和 j 段了
len = n / k;
转移就是 :
dp[i][j] = max (dp[i -1][j] + a[k + 1] - a[k],dp[i][j]);
k = (i - 1) * (len + 1) + j * len;
dp[i][j] = max (dp[i][j -1] + a[k + 1] - a[k],dp[i][j]);
k = i * (len + 1) + (j - 1) * len;
这样就 ok 了
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
using namespace std;
const int maxn = 5005;
const int INF = 1E9 + 7;
int n,k;
int dp[maxn][maxn] = {0};
int a[300005] = {0};
int num1,num2;
int main () {
ios_base :: sync_with_stdio(false);
cin >> n >> k;
for (int i = 1;i <= n; ++ i) {
cin >> a[i];
}
sort (a + 1,a + n + 1);
num1 = n % k;
num2 = k - n % k;
int len = n / k;
for (int i = 0;i <= k + 1; ++ i) {
for (int j = 0;j <= k + 1; ++ j) dp[i][j] = -INF;
}
dp[1][0] = dp[0][1] = 0;
for (int i = 0;i <= num1; ++ i) {
for (int j = 0;j <= num2; ++ j) {
if (i) {
int k = (len + 1) * (i - 1) + j * len;
dp[i][j] = max (dp[i][j],dp[i - 1][j] + a[k + 1] - a[k]);
}
if (j) {
int k = (len + 1) * i + len * (j - 1);
dp[i][j] = max (dp[i][j],dp[i][j - 1] + a[k + 1] - a[k]);
}
}
}
int ans = a[n] - a[1] - dp[num1][num2];
cout << ans << endl;
return 0;
}