题目大意
现在有 \(n\) 个学生,每个学生的水平为 \(a_i\),允许你往其中添加 \(k\) 个任意水平的学生。现在要求你把这 \(n\) 个学生分成几组,使得每一组中的水平大小相邻的学生的水平绝对差小于等于 \(x\)。
思路分析
考虑贪心
可以先对学生的水平排一下序,算绝对差,把绝对差存储到一个数组里,接着对这个绝对差数组做贪心。
如何贪心
因为绝对差小的情况更容易满足,所以我们要先对绝对差数组排序,使得绝对差小的情况先考虑,绝对差大的情况后考虑。
如果绝对差大于 \(x\),可以用两种办法解决:
- 能用加学生解决的,就用加学生解决。(并且要使得加的学生尽量少)
- 不能用加学生解决的,可以直接分成两组,避免两个人在同一组内。
代码剖析
3.1:差分
sort(a+1,a+n+1);
for(int i=2;i<=n;i++){
b[i-1]=a[i]-a[i-1];
}
sort(b+1,b+n);
\(b\) 数组存储了两个水平相邻学生的绝对差,这里可以不带绝对值,因为 \(sort\) 的结果是从小到大的。
最后对 \(b\) 数组排序,以便后面做贪心。
3.2:贪心
for(int i=1;i<n;i++){
if(b[i]>x){//如果需要操作
if(k>=(b[i]-1)/x){//看一看可不可以通过加学生解决
k-=(b[i]-1)/x;//可以就让可用学生减掉需要学生
}
else{
cnt++;//否则就分组,分组的次数加1。
}
}
}
首先判断这个差是否需要操作,如果不需要,就不管了。
如果需要,先尝试用加学生的办法去做。
- WARNING:这里减掉 \(1\) 是非常重要的。我们可以把学生看成隔板,把一段水平差分成几段,而段数比点数少 \(1\)。
如果学生够用,把可用学生数量减去需要的学生数量。
否则就分一次组,让分组次数的计数器加 \(1\)。
最后直接输出。
- WARNING:输出 \(cnt+1\)!因为 \(cnt\) 记录的是分组的次数,分了 \(cnt\) 次,就会有 \(cnt+1\) 组。
AC Code
代码已分段给出,不给出完整代码