• J 分班(class)(NYIST 2019年校赛)



    • J 分班(class)(NYIST 2019年校赛)

    内存限制:256MB 时间限制:1s Special Judge: No

    题目描述:

    jsb 是 XX 市第一中学的校长。一轮模拟考试结束后,jsb 想让所有学生重新分班。

    XX 市第一中学共有 n 位学生,其中第 i 个学生的该次模拟考试的成绩为 bi。jsb 打算将所 有学生分配到 m 个班级里,第 i 个班级的人数为 ai,即需要将恰好 ai 个人分配到第 i 个班级 内。

    然而,如果班级里的学霸和班级内的其他同学水平差距过大,会使学霸产生自满度。具体 来说,如果第 i 个学生在第 j 个班级中,他的成绩比第 j 个班级的所有其他人的分数最大值 还高,那么的自满度等于他的成绩减去第 j 个班级第二名的成绩;否则他的自满度为 0。

    jsb 希望所有学生的自满度的最大值尽量小。请你安排分班方案,使得所有学生自满度的 最大值最小。

    例如,假设一共有 10 位学生,他们的成绩分别为 1,2,3,4,5,6,7,8,9,10,现在需要将他们 分到人数分别为 4 和 6 的两个班级内。可以把成绩为 1,2,3,4 的同学分成一个班,成绩为 5,6,7,8,9,10 的同学分成一个班,这样成绩为 4 和 10 的同学自满度为 1,其他同学的自满度 为 0,最大的自满度为 1。显然,最大的自满度不可能小于 1;当然,使自满度最大值为 1 的 分班方法还有其他很多种。

    输入描述:

    第一行 2 个正整数 n, m (2 ≤ n ≤ 100000, 1 ≤ m ≤ 50000),表示学生人数以及班级的个数。
    
    接下来一行 m 个正整数 a1, a2, ..., am (ai ≥ 2,∑m i=1 ai = n) ,表示每个班级的人数。
    
    接下来一行 n 个正整数 b1, b2, ..., bn (0 ≤ bi ≤ 109),表示每个人的成绩。
    

    输出描述:

    一行一个正整数,表示答案,即所有学生的自满度最大值的最小值。
    

    样例输入:

    10 2
    4 6
    1 2 3 4 5 6 7 8 9 10
    

    样例输出:

    1
    

    提示:

    img

    分析:

    ​ 可以采用二分求解。

    下面解释为什么可以使用二分求解

    ​ 假设答案的所要求的最小值为kk, 现在测试valmaxvalmax为分班后的自满度最大值

    • 如果valmax<kvalmax<k 则不管采用何种方法进行分班肯定不成功的
    • 如果valmax>kvalmax>k,则最优的分班方法可定能成功分班

    那么对于一个valmaxvalmax怎么去检验该值是否可行?

    下面设计一个最优的分班方法:

    ​ 我们可以将学生成绩从小到大排序,将班级人数从小到大排序,接下来 开始从大到小开始遍历学生成绩,如果挨着的两个学生成绩之差<=valmax<=valmax 则将这一对作为某一个班级的前二名,就这样一直下去,直到遍历完所有学生,或者选出的对数已经等于班级个数则退出遍历。下面即开始检验分班人数是否满足.

    ​ 记录下每对前面可选的人数(还未分到班级的人数)。 可以这样计算:假设班级共mm个,总共有nn个人,这一组前面还有ii个人,这是第tottot对,那么该对下前面未分班的人数为(i(mtot))2)(i-(m-tot))*2)

    ​ 按照先前选出对的顺序分别作为第m,m1,m2,...1m,m-1,m-2, ...1个班的前二名 (第mm班的班级人数最多,第m1m-1的班级人数次多,依此类推)

    ​ 如果对于第ii个班 可以分够人数,则需要满足下面两点要求

    • ​ 前i个班可以分够人数

    • ​ 如果前i个班的需要塞的人数(即不包括每个班前二名)<=第i个班前二名之前的人数(即还可以塞的人数) ,

      那么则该班可以分够要求的人数。

    ​ 如果所有的班都可以分够要求的人数,则该maxval可行。

    代码:

    #include<bits/stdc++.h>
    #define mset(a,b)   memset(a,b,sizeof(a))
    using namespace std;
    const int maxn=1e5+100;
    int total[maxn];//班级需要分的人数 从大到小
    int frontot[maxn];//从大到小  表示 后面未分班的学生
    int score[maxn<<1],book[maxn];
    int m,n;
    bool cmp(int a,int b)
    {
        return a>b;
    }
    bool Check(int val)
    {
    /*
    1.符合要求的对数,到达m对即可
    2.再记下m对前面可以塞的个数
    */
        int tot=0;
        mset(book,0);
        for(int i=n-2;i>=0;--i)
        {
            if(book[i+1]||book[i])
                continue;
            if(score[i+1]-score[i]<=val)
            {
                frontot[tot]=i-(m-tot-1)*2;
                tot++;
                if(tot==m)
                    break;
                book[i+1]=book[i]=1;
            }
        }
        if(tot<m)
            return 0;
        int sum=0;
        for(int i=m-1;i>=0;--i)
        {
            sum+=total[i]-2;
            if(frontot[i]<sum)
                return 0;
        }
        return 1;
    }
    int main()
    {
        scanf("%d%d",&n,&m);
        for(int i=0;i<m;++i)
        {
            scanf("%d",total+i);
        }
        for(int i=0;i<n;++i)
            scanf("%d",score+i);
        sort(score,score+n);
        sort(total,total+m,cmp);
        int mid,ans=-1,l,r;
        l=0,r=1e9;
        while(l<=r)
        {
            mid=(l+r)>>1;
            if(Check(mid))
            {
                r=mid-1;
                ans=mid;
            }
            else
            {
                l=mid+1;
            }
    
        }
        printf("%d
    ",ans);
        return 0;
    }
    
    
  • 相关阅读:
    Oracle 查看表空间的使用情况SQL语句
    汇总查询
    conky配置2
    数据库更新
    weka简介和回归转自chinakdd
    子查询
    ubuntu常用命令
    查询
    数据库中的连接
    测试用的数据库表及其数据
  • 原文地址:https://www.cnblogs.com/dchnzlh/p/10546537.html
Copyright © 2020-2023  润新知