【题目描述】
有 \(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 \text {s}\)
空间限制:\(512 \text {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;
}