(二分+扫描线)
小学生一发
一道典型的二分加扫描线的题目。
人以群分
题目描述:
某班有
个同学,每个同学有一个外向程度
。由于要进行某个活动,需要把他们分成若干个小组,每个小组的人数至少为
人。不同外向程度的人在一个小组会产生不开心值,定义一个小组的不开心值为组内成员外向程度最大值和最小值的差。一个班级的不开心值为所有小组不开心值的最大值。
那么问题来了,如何分组使得班级的不开心值最小,请你求出这个最小的班级不开心值。
输入:
第一行两个整数
, 分别表示人数和每个小组最少的人数要求。
第二行
个整数
,表示每个同学的外向程度。
输出:
一个整数,表示最小的班级不开心值。
解析
看到题目要求最大值的最小值,这类题一般都是对答案进行二分,然后检查每个二分后的是是否满足题目的要求。本题也不例外,先将数组 进行排序, 二分区间为 。然后就是检查每个值是否符合要求,检查的策略为对于每个 ,考虑将数组 分成的若干份,每份的最大差值小于等于 的情况下,是否满足每份的个数均大于 。检查即对每个 ,找到最大满足 的位置 ,将区间 染色,利用扫描线数组 来完成这个过程,最后只需要检查 是否大于0即可。
AC代码:
// 小学生一发的刷题之路
// 人以群分
// 二分法+扫描线数组;
//
//
#include <iostream>
#include <cstdio>
#include <string>
#include <cstring>
#include <algorithm>
#include <queue>
#include <deque> //双向队列;
#include <cmath>
#include <set>
#include <stack>
#include <map>
#include <vector>
#include <cstdlib>
#include <iomanip>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef long double ld;
const double PI=acos(-1.0);
const double eps=1e-8;
const int maxn=5e5+5;
const int maxm=1e6+5;
const ll mod=1e9+7;
const int INF=1e8;
template<class T>
void read(T &ret){ //快速输入模版;
ret=0;
int f=1;
char c=getchar();
while(c<'0'||c>'9'){
if(c=='-') f=-1;
c=getchar();
}
while(c>='0'&&c<='9'){
ret=ret*10+c-'0';
c=getchar();
}
ret*=f;
}
int n,m,a[maxn],dp[maxn];
bool check(int x){
memset(dp,0,sizeof(dp));
dp[0]=1,dp[1]=-1;
int sum=0,l,r,j=1;
for(int i=0;i<=n;i++){
sum+=dp[i];
if(sum==0){
continue;
}
while(j<=n&&a[j]-a[i+1]<=x){
j++;
}
l=i+m;
r=j;
if(l>=r){ //无需染色。
continue;
}
dp[l]++;
dp[r]--;
}
return sum>0;
}
int main()
{
scanf("%d %d",&n,&m);
for(int i=1;i<=n;i++){
read(a[i]);
}
sort(a+1,a+1+n);
int l=0,r=a[n]-a[1];
while(l<r){
int mid=(l+r)/2;
if(check(mid)){
r=mid;
}else{
l=mid+1;
}
}
printf("%d\n",l);
return 0;
}
新的开始,每天都要快乐哈。