某班有 nn 个同学,每个同学有一个外向程度 a_iai。由于要进行某个活动,需要把他们分成若干个小组,每个小组的人数至少为 mm 人。不同外向程度的人在一个小组会产生不开心值,定义一个小组的不开心值为组内成员外向程度最大值和最小值的差,一个班级的不开心值为所有小组不开心值的最大值。
那么问题来了,如何分组使得班级的不开心值最小,请你求出这个最小的班级不开心值。
第一行两个整数 n,mn,m,分别表示人数和每个小组最少的人数要求。
第二行 nn 个整数 a_iai,表示每个同学的外向程度。
一个整数,表示最小的班级不开心值。
对于 30\%30% 的数据:1\le m \le n \le 201≤m≤n≤20,1\le a_i \le 1001≤ai≤100。
对于 60\%60% 的数据:1\le m \le n\le 10001≤m≤n≤1000,1\le a_i \le 10001≤ai≤1000。
对于 100\%100% 的数据:1\le m\le n \le 5\cdot10^51≤m≤n≤5⋅105,1\le a_i \le 10^91≤ai≤109。
第一个样例,只要每个人各自一个组,不开心值就都是 00。
第二个样例,最佳的分组情况为:9,119,11 一个组,6,3,56,3,5 一个组,两个组的不开心值分别为 22 和 33,那么班级的不开心值为 33。
样例输入1复制
5 1 2 4 6 8 10
样例输出1复制
0
样例输入2复制
5 2 9 11 6 3 5
样例输出2复制
3
题目来源
2019 蓝桥杯省赛 A 组模拟赛(一)
地址:https://nanti.jisuanke.com/t/A2229
思路:二分+DP
先对不快乐值a[n]由小到大排序,再对答案h二分,对于二分的判断judge(h)需要用到DP,先谈谈我的做法,
dp[i]:为1表示前i个可以分组,为0表示不可分组,那么当 i-k>=m,a[i]-a[k+1]<=h时dp[k]中存在1,则dp[i]=1
dp[i]=dp[k] (a[i]-a[k+1]<=h,i-k>=m),当dp[k]存在1时则dp[i]=1,否则为0
对于dp[k]的判断可以用前缀和来避免循环查找
另一种DP思路 *【计蒜客 - 蓝桥训练】人以群分(二分 + dp)
dp[i]:表示前i个a[i]可以分组的最后一个人的编号(即编号1-dp[i]都可以成功分组)
那么对于dp[i],当编号dp[i-m]+1->a[i]间不大于h时,即a[i]-a[dp[i-m]+1]<=h时dp[i]=i;否则dp[i]=dp[i-1];
Code 1:
#include
#include
#include
using namespace std;
typedef long long LL;
const int MAX_N=5e5+5;
int n,m;
LL a[MAX_N];
int L[MAX_N],dp[MAX_N];
bool judge(LL h);
int main()
{
ios::sync_with_stdio(false);
cin>>n>>m;
for(int i=1;i<=n;++i)
cin>>a[i];
sort(a+1,a+n+1);
LL l=0,r=1e10,h;
while(l<=r){
h=(l+r)/2;
if(judge(h)) r=h-1;
else l=h+1;
}
cout<=0){
if(L[i]-2<0&&dp[i-m]) ++dp[i];
if(L[i]-2>=0&&dp[i-m]-dp[L[i]-2]>0) ++dp[i];
}
}
if(dp[n]-dp[n-1]==0) boo=false;
return boo;
}
Code 2:
#include
#include
using namespace std;
typedef long long LL;
const int MAX_N=5e5+5;
int n,m;
LL a[MAX_N];
int dp[MAX_N];
bool judge(LL h);
int main()
{
ios::sync_with_stdio(false);
cin>>n>>m;
for(int i=1;i<=n;++i)
cin>>a[i];
sort(a+1,a+n+1);
LL l=0,r=1e10,h;
while(l<=r){
h=(l+r)/2;
if(judge(h)) r=h-1;
else l=h+1;
}
cout<