Medium
题目描述
给你一个编号球的集合,要求将它们排序到盒子中以实现近乎平衡的分布。你必须遵循两个规则:
- 同一个盒子中的球必须有相同的值。但是,如果你有多个相同编号的球,你可以把它们放在不同的盒子中。
- 最大的盒子最多只能比最小的盒子多一个球。
返回遵循这些规则排序这些球所需的最少盒子数。
示例 1:
输入:balls = [3,2,3,2,3]
输出:2
解释:
我们可以将球分组如下:
[3,3,3]
[2,2]
两个盒子的大小差异不超过一。
示例 2:
输入:balls = [10,10,10,3,1,1]
输出:4
解释:
我们可以将球分组如下:
[10]
[10,10]
[3]
[1,1]
在遵循规则的情况下,你不能使用少于四个盒子。例如,将所有三个编号为10的球放在一个盒子中会违反盒子之间最大大小差异的规则。
约束条件:
1 <= balls.length <= 10^51 <= balls[i] <= 10^9
解题思路
这道题的核心思路是贪心算法结合数学计算。
首先理解题意:我们需要将相同数字的球分到多个组中,使得所有组的大小差异不超过1,并且组数最少。
解题步骤:
统计频率:统计每个数字出现的次数
枚举组大小:由于组大小差异不超过1,我们可以枚举最小组大小x,那么组大小只能是x或x+1
计算最优分组:对于每个数字的频率f和组大小x,计算最少需要多少个组:
- 设
a = f / (x + 1),b = f % (x + 1) - 如果
b == 0,可以创建a个大小为x+1的组 - 如果
x - b <= a,可以创建a - (x - b)个大小为x+1的组和x - b + 1个大小为x的组,总计a+1个组 - 否则无法满足条件
- 设
优化范围:x的取值范围是[1, min_frequency],因为如果x大于某个数字的频率,就无法分组了
时间复杂度主要在枚举x和计算每个频率的分组数,通过数学公式可以高效计算。
代码实现
class Solution {
public:
int minGroupsForValidAssignment(vector<int>& balls) {
unordered_map<int, int> freq;
for (int ball : balls) {
freq[ball]++;
}
int minFreq = INT_MAX;
for (auto& p : freq) {
minFreq = min(minFreq, p.second);
}
int result = INT_MAX;
for (int x = 1; x <= minFreq; x++) {
int totalGroups = 0;
bool valid = true;
for (auto& p : freq) {
int f = p.second;
int a = f / (x + 1);
int b = f % (x + 1);
if (b == 0) {
totalGroups += a;
} else if (x - b <= a) {
totalGroups += a + 1;
} else {
valid = false;
break;
}
}
if (valid) {
result = min(result, totalGroups);
}
}
return result;
}
};
class Solution:
def minGroupsForValidAssignment(self, balls: List[int]) -> int:
from collections import Counter
freq = Counter(balls)
min_freq = min(freq.values())
result = float('inf')
for x in range(1, min_freq + 1):
total_groups = 0
valid = True
for f in freq.values():
a = f // (x + 1)
b = f % (x + 1)
if b == 0:
total_groups += a
elif x - b <= a:
total_groups += a + 1
else:
valid = False
break
if valid:
result = min(result, total_groups)
return result
public class Solution {
public int MinGroupsForValidAssignment(int[] balls) {
var freq = new Dictionary<int, int>();
foreach (int ball in balls) {
freq[ball] = freq.GetValueOrDefault(ball, 0) + 1;
}
int minFreq = freq.Values.Min();
int result = int.MaxValue;
for (int x = 1; x <= minFreq; x++) {
int totalGroups = 0;
bool valid = true;
foreach (int f in freq.Values) {
int a = f / (x + 1);
int b = f % (x + 1);
if (b == 0) {
totalGroups += a;
} else if (x - b <= a) {
totalGroups += a + 1;
} else {
valid = false;
break;
}
}
if (valid) {
result = Math.Min(result, totalGroups);
}
}
return result;
}
}
/**
* @param {number[]} balls
* @return {number}
*/
var minGroupsForValidAssignment = function(balls) {
const count = new Map();
for (const ball of balls) {
count.set(ball, (count.get(ball) || 0) + 1);
}
const frequencies = Array.from(count.values());
const minFreq = Math.min(...frequencies);
for (let k = minFreq; k >= 1; k--) {
let totalGroups = 0;
let valid = true;
for (const freq of frequencies) {
const fullGroups = Math.floor(freq / (k + 1));
const remainder = freq % (k + 1);
if (remainder === 0) {
totalGroups += fullGroups;
} else {
const needed = k - remainder;
if (needed <= fullGroups) {
totalGroups += fullGroups + 1;
} else {
valid = false;
break;
}
}
}
if (valid) {
return totalGroups;
}
}
return frequencies.length;
};
复杂度分析
| 复杂度类型 | 分析 |
|---|---|
| 时间复杂度 | O(n + k × m),其中n是balls数组长度,k是最小频率,m是不同数字的个数 |
| 空间复杂度 | O(m),用于存储频率统计,m是不同数字的个数 |