题目链接:https://vjudge.net/problem/%E8%AE%A1%E8%92%9C%E5%AE%A2-A2229
某班有 nn 个同学,每个同学有一个外向程度 a_ia
i
。由于要进行某个活动,需要把他们分成若干个小组,每个小组的人数至少为 mm 人。不同外向程度的人在一个小组会产生不开心值,定义一个小组的不开心值为组内成员外向程度最大值和最小值的差,一个班级的不开心值为所有小组不开心值的最大值。
那么问题来了,如何分组使得班级的不开心值最小,请你求出这个最小的班级不开心值。
输入格式
第一行两个整数 n,mn,m,分别表示人数和每个小组最少的人数要求。
第二行 nn 个整数 a_ia
i
,表示每个同学的外向程度。
输出格式
一个整数,表示最小的班级不开心值。
数据范围
对于 30%30% 的数据:1\le m \le n \le 201≤m≤n≤20,1\le a_i \le 1001≤a
i
≤100。
对于 60%60% 的数据:1\le m \le n\le 10001≤m≤n≤1000,1\le a_i \le 10001≤a
i
≤1000。
对于 100%100% 的数据:1\le m\le n \le 5\cdot10^51≤m≤n≤5⋅10
5,1<=ai<=109,1≤a i ≤109
。
样例解释
第一个样例,只要每个人各自一个组,不开心值就都是 0。
第二个样例,最佳的分组情况为: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
emmmmmmmm,题目复制过来显示就不太好了,还是看链接的体面吧。
分析:
一开始我以为是啥子贪心啊,思维题啊,但是……刚了好久,实在是每思路了,然后去找题解了,高高兴兴搜题解呀,那叫一个高兴啊,高…高…兴……………………啊啊啊啊啊啊啊
原谅我实在是没有一眼就看懂这位大佬的题解(此时的我留下了不学无术的眼泪,太菜了)https://blog.csdn.net/qq_41289920/article/details/87032762
大佬是用DP+二分来实现的(这里只是第一个思路,第二个还没看QAQ);
dp[i]代表前i个人已经分好组之后,最后一个可以加入当前分组的人的编号(这里的最后一个人指的是当前组的最后,不一定是第n个人,这点要搞清楚)
二分是用来二分答案的,对于每一个答案,用dp去判断是不是符合条件,也就是当前的二分测试答案X是不是所有组中不开心值的最大值。
下面是我手写的分析过程,可能有些地方理解起来还是有点困难,最好多看几遍。
AC代码
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
#define inf 0x3f3f3f3f
#define rep(i,l,r) for(int i=l;i<=r;i++)
#define lep(i,l,r) for(int i=l;i>=r;i--)
#define ms(arr) memset(arr,0,sizeof(arr))
//priority_queue ,greater >q;
const int N = 1e5 + 10;
const int mod = 1e9+7;
int dp[N*5],n,m,a[N*5];
bool Check(int x){
dp[m]=m;
for(int i=m+1;i<=n;i++){
if(a[i]-a[dp[i-m]+1]<=x) dp[i]=i;
else dp[i]=dp[i-1];
}
return dp[n]==n;
}
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++) scanf("%d",&a[i]);
sort(a+1,a+1+n);
int l=0,r=inf;
while(l<=r){
int mid=(l+r)>>1;
if(Check(mid))
r=mid-1;
else
l=mid+1;
}
printf("%d\n",r+1);
return 0;
}