题目链接:http://codeforces.com/problemset/problem/572/D
当k=1的时候,从小到大排序求相邻差值的和,那就是最小值
当k>1的时候,先看个例子
数列1 4 5 8 10 k=2
那么相邻的差值之和就是10
那么可以分成2个数字一段,3个数字一段
如果结果是1 4 5 8 10 最终的差值和就是10-3=7
如果结果是1 4 5 8 10 最终的差值和就是10-1=9
用dp做
dp[i][j] i表示总的段数 j表示较短的段数
#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <string>
using namespace std;
#define inf 300005
#define ll __int64
ll p[inf];
ll dp[5001][5001];
int main()
{
int n,k;
memset(p,0,sizeof(p));
memset(dp,0,sizeof(dp));
scanf("%d%d",&n,&k);
for(int i=1;i<=n;i++)
{
scanf("%I64d",&p[i]);
}
sort(p+1,p+1+n);
ll sum=0;
for(int i=2;i<=n;i++)
{
sum+=p[i]-p[i-1];
}
int n1,n2,num1,num2;
n1=n/k;//较短一段的数字的个数
n2=n/k+min(1,n%k);//较长一段的数字的个数
num2=n%k;//较长一段的段数
num1=(n-num2*n2)/n1;//较短一段的段数
ll ans=0;
for(int i=1;i<num1;i++)
{
ans+=p[i*n1+1]-p[i*n1];
dp[i][i]=ans;
}
if(num2>0)
{
dp[num1][num1]=ans+p[num1*n1+1]-p[num1*n1];
}
if(num2==0) dp[num1][num1]=ans;
else
{
for(int i=1;i<=num1+num2;i++)
{
for(int j=max(0,i-num2);j<=min(i,num1);j++)
{
if(i==j)
{
continue;
}
ll ans=p[(i-j)*n2+j*n1+1]-p[(i-j)*n2+j*n1];
if(ans<0) ans=0;
dp[i][j]=dp[i-1][j]+ans;
if(j>=1 && i-j<=num2)
{
dp[i][j]=max(dp[i][j],dp[i-1][j-1]+ans);
}
}
}
}
printf("%I64d\n",sum-dp[num1+num2][num1]);
return 0;
}