题目链接: 分组
首先用一个哈希表保存每个声部出现的次数, 并用一个变量保存出现次数最多的声部的出现次数, 此时判断一下哈希表中元素的个数, 如果该个数大于 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;
}