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
became minimal possible. In particular, it is allowed not to change order of elements at all.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; }