You've got array A, consisting of n integers and a positive integer k. Array A is indexed by integers from 1 to n.
You need to permute the array elements so that value

The first line contains two integers n, k (2 ≤ n ≤ 3·105, 1 ≤ k ≤ min(5000, n - 1)).
The second line contains n integers A[1], A[2], ..., A[n] ( - 109 ≤ A[i] ≤ 109), separate by spaces — elements of the array A.
Print the minimum possible value of the sum described in the statement.
3 2 1 2 4
1
5 2 3 -5 3 -5 3
0
6 3 4 3 4 3 2 5
3
In the first test one of the optimal permutations is 1 4 2.
In the second test the initial order is optimal.
In the third test one of the optimal permutations is 2 3 4 4 3 5.
题意很简单,就是要求一个数列重排,使得sum{ | a[i+k] - a[i] |}最小。
首先考虑,i i + k,i + 2 * k ,i + 3 * k,这样的数,看成一个组的话,s1 s2 s3 .... sn.如何使这个集合最小呢,自然是s1 s2 s3 .. . sn是递增的,最小为sn - s1.如果不是递增的,最大值在中间,那么从头到最大值这段的和,已经比sn - s1更大了。所以首先可以得到递增的性质,那么就可以先排个序,那么我们可以得到有n / k长度的段,一共有 k - n % k个,n /k + 1长度的段,一共有 n % k个。其次,要sn - s1最小,自然,sn与s1越接近,总和最小,所以每段都会是一个连续段。当然这是一个贪心的思想。因为,如果不是连续的,那么,我们一定可以交换成连续的段,使得总和最小,任何一个连续段,交换任何数,变成不连续段,都会使总和增大,为什么,因为,交换了之后,小的那一段,最大值增大了,大的那一段,最小值增大了,小的 大的两段都增大了,所以总和就增大了。可以发现这种策略是正确的。
得到了每组都是连续段的结论,就好做了,用dp来做,dp[i][j]表,已经有i段长为n /k 的组,j段长为n / k + 1 的组所能得到的最小值。
dp[i][j] = min(dp[i][j],dp[i-1][j] + pri[now] - pri[now - l1 + 1]);//最后一组是长为l1的组
dp[i][j] = min(dp[i][j],dp[i][j-1] + pri[now] - pri[now - l2 + 1]);//最后一组是长为l2的组
状态有o(k * k)个。转移为o(1),加上排序,总的复杂度为o(n * logn + k * k);
#define N 300005
#define M 5005
#define maxn 205
#define MOD 1000000000000000007
int n,k,t;
ll pri[N],dp[M][M];
int main()
{
//freopen("in.txt", "r", stdin);
//freopen("out.txt", "w", stdout);
while(S2(n,k)!=EOF)
{
FI(n){
S(t);pri[i] = t;
}
if(n < k){
printf("0\n");
continue;
}
sort(pri,pri+n);
int x1 = k - n % k,x2 = n % k,l1 = n/k,l2 = n/k + 1;
FI(x1 + 1){
FJ(x2 + 1){
dp[i][j] = MOD;
}
}
dp[0][0] = 0;
FI(x1 + 1){
FJ(x2 + 1){
int now = i * l1 + j * l2 - 1;
if(i >= 1)
dp[i][j] = min(dp[i][j],dp[i-1][j] + pri[now] - pri[now - l1 + 1]);
if(j >= 1)
dp[i][j] = min(dp[i][j],dp[i][j-1] + pri[now] - pri[now - l2 + 1]);
}
}
printf("%lld\n",dp[x1][x2]);
}
//fclose(stdin);
//fclose(stdout);
return 0;
}