[算法题]分组

题目链接: 分组

首先用一个哈希表保存每个声部出现的次数, 并用一个变量保存出现次数最多的声部的出现次数, 此时判断一下哈希表中元素的个数, 如果该个数大于 m, 根据题意在一个小组的人必须同声部, 而不同声部的组数都大于 m 了, 那么必然无法顺利安排, 返回 -1 即可, 反之, 从 1 遍历到出现次数最多的声部的出现次数, 循环变量表示每组的人数, 通过遍历哈希表中的元素与循环变量相除, 商即为每组有 i 人, 需要多少组, 如果有余数视为要在商的基础上加上一组, 为 0 则表示不用, 每轮循环变量累加完组数后与 m 比较, 小于等于 m 则会题解, 反之继续遍历.

题解代码:
 

#include <iostream>
#include <unordered_map>
using namespace std;

int main()
{
    int n, m;
    cin >> n >> m;
    unordered_map<int, int> hash; //key:数值 val:key出现次数
    int num = 0;
    int max_cnt = 0; //记录最大出现次数
    for(int i = 0; i < n; ++i)
    {
        cin >> num;
        max_cnt = max(++hash[num], max_cnt);
    }
    
    //判断是否可以顺利安排组数
    if(hash.size() > m)
    {
        cout << -1 << endl;
    }
    else
    {
        for(int i = 1; i <= max_cnt; ++i)
        {
            int cnt = 0;
            for(auto& [_, val] : hash)
            {
                cnt += (val / i) + (val % i ? 1 : 0);
            }
            if(cnt <= m)
            {
                cout << i << endl;
                break;
            }
        }
    }
    return 0;
}

可以通过二分来优化遍历, 不必从 1 遍历到出现次数最多的声部的出现次数, 定义变量 left=1, right=出现次数最多的声部的出现次数, 每次取它们的中间值做当前每组安排几人, 这样可以快速排除一半的区间, 当累加的组数大于 m 时, 表示当前每组安排的人数比较少, 会超过规定的组数 m, 那么只需让 left = mid+1 即可, 如果当累加的组数小于等于 m 时, 表示当前分组是可行的, 但不一定满足题意的小组人数尽可能少的要求, 所以让 right = mid 继续遍历, 指导 left == right, 那么题解就为 left/right, 优化代码如下:

#include <iostream>
#include <unordered_map>
using namespace std;

int main()
{
    int n, m;
    cin >> n >> m;
    unordered_map<int, int> hash; //key:数值 val:key出现次数
    int num = 0;
    int max_cnt = 0; //记录最大出现次数
    for(int i = 0; i < n; ++i)
    {
        cin >> num;
        max_cnt = max(++hash[num], max_cnt);
    }
    
    //判断是否可以顺利安排组数
    if(hash.size() > m)
    {
        cout << -1 << endl;
    }
    else
    {
        int left = 1;
        int right = max_cnt;
        while(left < right)
        {
            int mid = left + (right - left) / 2;
            int cnt = 0;
            for(auto& [_, val] : hash)
            {
                cnt += (val / mid) + (val % mid ? 1 : 0);
            }
            if(cnt <= m)
            {
                right = mid;
            }
            else
            {
                left = mid + 1;
            }
        }
        cout << left << endl;
    }
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值