【编程题-错题集】分组(枚举 + 二分)

简介: 【编程题-错题集】分组(枚举 + 二分)

牛客对应题目链接:分组 (nowcoder.com)


一、分析题目

1、暴力枚举

从小到大枚举所有可能的最大值,找到第⼀个符合要求的最大值。

  1. 统计出每一种声部的人数:hash。
  2. 枚举最终的分配结果中,最多的人数(x),从小到大枚举 1~hmax


2、二分优化枚举

利用二段性,⼆分出符合要求的最大值。


二、代码

1、方法一(暴力枚举)

#include <iostream>
#include <unordered_map>
 
using namespace std;
 
int n, m;
unordered_map<int, int> cnt; // 统计每种声部的⼈数
 
bool check(int x) // 判断最多⼈数为 x 时,能否分成 m 组
{
    int g = 0; // 能分成多少组
    for(auto& [a, b] : cnt)
    {
        g += b / x + (b % x == 0 ? 0 : 1);
    }
    return g <= m;
}
 
int main()
{
    cin >> n >> m;
    int hmax = 0; // 统计声部最多的⼈数
    for(int i = 0; i < n; i++)
    {
        int x;
        cin >> x;
        hmax = max(hmax, ++cnt[x]);
    }
 
    int kinds = cnt.size();
    if(kinds > m) // 处理边界情况
    {
        cout << -1 << endl;
    }
    else
    {        
        // 暴⼒枚举
        for(int i = 1; i <= hmax; i++) // 枚举所有的最多⼈数
        {
            if(check(i))
            {
                cout << i << endl;
                break;
            }
        }
    }
 
    return 0;
}

2、方法二(二分)

#include <iostream>
#include <unordered_map>
 
using namespace std;
 
int n, m;
unordered_map<int, int> cnt; // 统计每种声部的⼈数
 
bool check(int x) // 判断最多⼈数为 x 时,能否分成 m 组
{
    int g = 0; // 能分成多少组
    for(auto& [a, b] : cnt)
    {
        g += b / x + (b % x == 0 ? 0 : 1);
    }
    return g <= m;
}
 
int main()
{
    cin >> n >> m;
    int hmax = 0; // 统计声部最多的⼈数
    for(int i = 0; i < n; i++)
    {
        int x;
        cin >> x;
        hmax = max(hmax, ++cnt[x]);
    }
 
    int kinds = cnt.size();
    if(kinds > m) // 处理边界情况
    {
        cout << -1 << endl;
    }
    else
    {        
        // ⼆分解法
        int l = 1, r = hmax;
        while(l < r)
        {
            int mid = (l + r) / 2;
            if(check(mid)) r = mid;
            else l = mid + 1;
        }
        cout << l << endl;
    }
 
    return 0;
}

三、反思与改进

这道题的暴力枚举思路基本正确,也想到了如何去分组才是正确的,不过没有理清楚在什么样的情况下会输出 -1 以及如何 +1 个分组的写法。虽然这道题目的暴力解法可以通过,但还是需要掌握二分优化的写法,发现自己虽然会写二分,但是对于二分的运用还是没有什么思路上的框架,下次遇到需要 check 的题目就往二分上想一想,看看能不能找出其中的二段性。


相关文章
|
3月前
|
C++
【洛谷 P2241】统计方形(数据加强版)题解(循环枚举)
该题目是1997年普及组的一道编程题,要求计算$n\times m$棋盘中的正方形和长方形数量(不计正方形)。输入包含两正整数$n,m\leq 5000$。输出为一行,两个正整数分别表示正方形和长方形数量。示例输入`2 3`,输出`8 10`。解题思路是将矩形数拆分为正方形数和长方形数,然后通过双重循环计算。AC代码使用C++编写,通过累加方法得出结果。
29 0
|
4月前
|
C语言
c语言编程练习题:7-51 求奇数分之一序列前N项和
c语言编程练习题:7-51 求奇数分之一序列前N项和
55 0
|
4月前
【错题集-编程题】排序子序列(模拟)
【错题集-编程题】排序子序列(模拟)
|
4月前
|
算法 搜索推荐 程序员
第四十六练 请以递归方式实现计算整数列表的和
第四十六练 请以递归方式实现计算整数列表的和
38 2
|
4月前
蓝桥备战-区间嵌套--前缀和做法
蓝桥备战-区间嵌套--前缀和做法
39 0
|
10月前
|
算法
代码随想录算法训练营第二十六天 | LeetCode 39. 组合总和、40. 组合总和 II、131. 分割回文串
代码随想录算法训练营第二十六天 | LeetCode 39. 组合总和、40. 组合总和 II、131. 分割回文串
39 0
|
11月前
|
人工智能 算法 测试技术
[蓝桥杯] 枚举、模拟和排列问题
[蓝桥杯] 枚举、模拟和排列问题
70 0
|
存储 C语言
C语言刷题系列——14.(结构)计算两个复数之积15.按等级统计学生成绩16.根据成绩高低将学生记录排序
C语言刷题系列——14.(结构)计算两个复数之积15.按等级统计学生成绩16.根据成绩高低将学生记录排序
122 0
|
算法
组合排序回溯编程题集合(leetcode)
组合排序回溯编程题集合(leetcode)