You've got array A, consisting of n integers and a positive integer k. ArrayA 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 arrayA.
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.
【参考blog】http://blog.csdn.net/u014800748/article/details/47905237
【题意】给定一个长度为n的序列,给定一个1<=k<=min(5000,n-1),通过重新排列,求最小的
首先考虑如何才能让一个组的结果对答案的贡献尽量小。由于组内的数都是原数组中每隔k个数取的,因此,对答案的贡献就是sum{Ai-Ai+1|i from 0 to m-1,m是该组的元素个数}。可以发现,只有当组内元素都从小到大排列之后,该组的贡献值最小,等于abs(Am-1-A0),这就告诉我们,首先要对原数组进行排序。我们定义状态dp(i,j)表示一共有i个组,其中j个为大组时的答案,那么不难得到如下的状态转移方程:
dp(i+1,j+1)=min(dp(i+1,j+1),dp(i,j)+a[idx+small]-a[idx]);
dp(i+1,j)=min(dp(i+1,j),d(i,j)+a[idx+small-1]-a[idx]);
其中,small=n/k, idx=i*s+j(因为j*(small+1)+(i-j)*small=i*small+j,表示下一个组的起始下标),那么如果作为大组处理,最后一个元素就是a[idx+small],作为小组处理,就是a[idx+small-1]。这样,最终的答案就是d(k,large)(large=n%k)。
[AC 代码]
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
typedef long long LL;
const LL inf = 1e15;
LL dp[5010][5010];
int a[300010];
int n,k;
int main()
{
scanf("%d%d",&n,&k);
for(int i=0; i<n; i++) scanf("%lld",&a[i]);
sort(a,a+n);
int small=n/k;//小组的个数
int large=n%k;//大组的最大组数
for(int i=0; i<k+1; i++)
for(int j=0; j<k+1; j++) dp[i][j]=inf;
dp[0][0]=0;
for(int i=0; i<k; i++){
for(int j=0; j<k; j++){
if(dp[i][j]==inf) continue;
int idx=i*small+j;//下一个组的起始下标
if(idx+small<n) dp[i+1][j+1]=min(dp[i+1][j+1],dp[i][j]+a[idx+small]-a[idx]);//下一个组为大组
if(idx+small-1<n) dp[i+1][j]=min(dp[i+1][j],dp[i][j]+a[idx+small-1]-a[idx]);//下一个组为小组
}
}
printf("%lld\n",dp[k][large]);
return 0;
}