题目描述
Evlampiy has found one more cool application to process photos. However the application has certain limitations.
Each photo i has a contrast vi. In order for the processing to be truly of high quality, the application must receive at least k photos with contrasts which differ as little as possible.
Evlampiy already knows the contrast vi for each of his n photos. Now he wants to split the photos into groups, so that each group contains at least k photos. As a result, each photo must belong to exactly one group.
He considers a processing time of the j-th group to be the difference between the maximum and minimum values of vi in the group. Because of multithreading the processing time of a division into groups is the maximum processing time among all groups.
Split n photos into groups in a such way that the processing time of the division is the minimum possible, i.e. that the the maximum processing time over all groups as least as possible.
输入
The first line contains two integers n and k (1 ≤ k ≤ n ≤ 3·105) — number of photos and minimum size of a group.
The second line contains n integers v1, v2, …, vn (1 ≤ vi ≤ 109), where vi is the contrast of the i-th photo.
输出
Print the minimal processing time of the division into groups.
样例输入1
5 2
50 110 130 40 120
样例输出1
20
样例输入2
4 1
2 3 4 1
样例输出2
0
题目大意为给定一组数,要求将其分成若干组,使其满足:每一组的元素个数大于等于k且每一组中元素的最大值与最小值之差尽可能小,并求出所有组中这个差值的最大值最小的情况。
首先,看到关键字最大值最小,可以考虑二分答案,那么问题的关键在于判断二分结果的正确性。一开始我是采用贪心的策略,将原数列升序排序之后从左往右扫一遍,遇到能够合并成一组的就合并,但这题加了一个元素数量大于等于k的限制,若只是单纯地扫一遍,可能会出现最后一组元素数量小于k的情况而无法处理。
因此,这里采用DP的方法。先将原数列升序排序,用一个数组f[i]表示前i个数字能否分成满足条件的若干组,假设当前二分结果是mid,我们转移f[i],因为元素最值之差要小于等于mid,我们从最前面开始往后查找,找到第一个a[j]使得a[i]-a[j]<=mid,这个j设为左边界l,因为同一个组中的元素个数要大于等于k,所以我们设置右边界r=i-k+1,那么以区间[l,r]内的任意元素a[j]为起点,a[i]为终点,区间[j,i]内的所有元素都可以分为满足条件的一组,那么只要满足f[j-1]=1即前j-1个元素可以分成满足条件的若干组,f[i]即为1。
即转移方程为f[i]=∪f[j-1] (j∈[l,r])。最后只要判断f[n]是否为1即可。总体时间复杂度约为O(nlogn)。
#include<bits/stdc++.h>
#define ll long long
#define next next_
#define y1 yy
using namespace std;
int n,k,a[300010],ans,f[300010];
bool ok(int t){
for(int i=1;i<=n;i++) f[i]=0;
f[0]=1;
int l=1,r;
for(int i=1;i<=n;i++){
while(a[i]-a[l]>t) l++;
r=i-k+1;
for(int j=l;j<=r;j++) if(f[j-1]){
f[i]=1;
break;
}
else l++;
}
return f[n];
}
int main(){
scanf("%d%d",&n,&k);
for(int i=1;i<=n;i++) scanf("%d",&a[i]);
sort(a+1,a+1+n);
int l=0,r=a[n];
while(l<=r){
int mid=l+r>>1;
if(ok(mid)){
ans=mid;
r=mid-1;
}
else l=mid+1;
}
printf("%d",ans);
return 0;
}