【题目描述】
有 (n) 个小朋友来食堂吃饭,他们排成长度为 (n) 的一个队伍。
小朋友看到别的小朋友比自己菜好会不高兴,两个小朋友i和j能互相看到对方的菜当且仅当 (operatorname{abs}(i-j)==k),产生的不高兴值是 (operatorname{abs}(a_i-a_j)),(operatorname{abs}) 是取绝对值符号,(a_i) 表示第i位小朋友菜丰盛的程度,换言之,如果菜的丰盛值按顺序排列是序列 (A),那么产生的不高兴值为
如果小朋友产生的不高兴值和太大他们会哭,你现在有 (n) 盘菜,请你合理安排上菜顺序,使得产生不高兴值的和最小,输出这个值。
【输入格式】
第一行输入两个正整数 (n),(k)。
第二行 (n) 个整数,表示第 (i) 盘菜的丰盛值 (a_i)。
【输出格式】
一个数表示最小的不高兴值和
【样例输入输出】
【输入 #1】
3 2
1 2 4
【输出 #1】
1
【输入 #2】
5 2
1 3 1 3 1
【输出 #2】
0
【输入 #3】
6 3
4 3 4 3 2 5
【输出 #3】
3
【数据范围与提示】
对于 (30\%) 的数据,满足 $n leq 10 $
对于 (50\%) 的数据,满足 $n,k leq 5000 $
对于另外 (30\%) 的数据,满足 (n) 是 (k) 的倍数
对于 (100\%) 的数据,满足 (n leq 300000,k leq min(n-1,5000), -10^9 leq a_i leq 10^9)
时间限制:(1 ext {s})
空间限制:(512 ext {MB})
题目可以转换为:在一个排好序的数列中,求几个区间相邻的每个数的差
因为每相邻 (k) 个求一次差,所以每个区间的长度只可能是 (n/k) 或 (n/k+1)。
(f_{i,j}) 表示取 (i) 个长度为 (n/k) 的区间和 (j) 个长度为 (n/k+1) 的区间的不高兴值的和最小值是多少
然后我们可以得到转移方程
(f[i+1][j]=min(f[i+1][j],f[i][j]+a[(i+1)*(n/k+1)+j*(n/k)]-a[i*(n/k+1)+j*(n/k)+1]))
(f[i][j+1]=min(f[i][j+1],f[i][j]+a[i*(n/k+1)+(j+1)*(n/k)]-a[i*(n/k+1)+j*(n/k)+1]))
然后就好了αωα
#include<bits/stdc++.h>
#define rint register long long
#define int long long
using namespace std;
inline int read(){
int s=0,f=1;char c=getchar();
while(c<'0'||c>'9'){if(c=='-')f=0;c=getchar();}
while(c>='0'&&c<='9')s=(s<<1)+(s<<3)+c-48,c=getchar();
return f?s:-s;
}
int n,k,a[300010],now,ans;
int f[5010][5010];
signed main(){
freopen("rice.in","r",stdin);
freopen("rice.out","w",stdout);
n=read(); k=read();
for(rint i=1;i<=n;++i) a[i]=read();
sort(a+1,a+1+n);
memset(f,0x3f,sizeof f);
f[0][0]=0;
int m_k1=n%k,m_k2=k-n%k;
for(int i=0;i<=m_k1;++i)
for(int j=0;j<=m_k2;++j){
if(i!=m_k1) f[i+1][j]=min(f[i+1][j],f[i][j]+a[(i+1)*(n/k+1)+j*(n/k)]-a[i*(n/k+1)+j*(n/k)+1]);
if(j!=m_k2) f[i][j+1]=min(f[i][j+1],f[i][j]+a[i*(n/k+1)+(j+1)*(n/k)]-a[i*(n/k+1)+j*(n/k)+1]);
}
printf("%lld",f[m_k1][m_k2]);
return 0;
}