二分法+扫描线

(二分+扫描线)

小学生一发

一道典型的二分加扫描线的题目。

人以群分

题目描述:
某班有 n n 个同学,每个同学有一个外向程度 a i a_i 。由于要进行某个活动,需要把他们分成若干个小组,每个小组的人数至少为 m m 人。不同外向程度的人在一个小组会产生不开心值,定义一个小组的不开心值为组内成员外向程度最大值和最小值的差。一个班级的不开心值为所有小组不开心值的最大值。

那么问题来了,如何分组使得班级的不开心值最小,请你求出这个最小的班级不开心值。

输入:
第一行两个整数 n , m n,m , 分别表示人数和每个小组最少的人数要求。
第二行 n n 个整数 a i a_i ,表示每个同学的外向程度。 ( m n 5 1 0 5 , 1 a i 1 0 9 ) (m \leq n \leq 5\cdot10^5, 1 \leq a_i \leq 10^9)

输出:
一个整数,表示最小的班级不开心值。

解析

看到题目要求最大值的最小值,这类题一般都是对答案进行二分,然后检查每个二分后的是是否满足题目的要求。本题也不例外,先将数组 a a 进行排序, 二分区间为 [ 0 , a n a 1 ] [0,a_n-a_1] 。然后就是检查每个值是否符合要求,检查的策略为对于每个 x x ,考虑将数组 a a 分成的若干份,每份的最大差值小于等于 x x 的情况下,是否满足每份的个数均大于 m m 。检查即对每个 a i a_i ,找到最大满足 ( a r a i + 1 ) x (a_r-a_{i+1}) \geq x 的位置 r r ,将区间 [ i + m , r ] [i+m,r] 染色,利用扫描线数组 d p [ i ] dp_{[i]} 来完成这个过程,最后只需要检查 s u m sum 是否大于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;
}

新的开始,每天都要快乐哈。
在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/weixin_39393640/article/details/87550509