0%

2910. 合法分组的最少组数

2910.合法分组的最少组数

题目描述:

给你一个长度为 n 下标从 0 开始的整数数组 nums

我们想将下标进行分组,使得 [0, n - 1] 内所有下标 i恰好 被分到其中一组。

如果以下条件成立,我们说这个分组方案是合法的:

  • 对于每个组 g ,同一组内所有下标在 nums 中对应的数值都相等。
  • 对于任意两个组 g1g2 ,两个组中 下标数量差值不超过 1

请你返回一个整数,表示得到一个合法分组方案的 最少 组数。

数据范围:

$1\le nums.len \le 10^5, 1\le nums[i] \le 10^9$

题解:

直接考虑贪心。先统计每种数的个数,然后构造一个新数组 $a$ 。

每组中的最大数量只能是频率最低的次数 $minx$ 。

直接从 $[minx, 1]$ 开始check,check成功的话就直接退出。

关键在于如何check,有两种策略。

  1. 先全凑成 $mid$ ,然后利用余数凑出来 $mid + 1$ 。然后考虑拆掉几组 $mid$ ,将前面的 $mid$ 凑成 $mid + 1$ 。
    • 假设凑成 $p$ 组 $mid$ ,余数为 $r$ 。则如果 $r > p$ ,返回 $-1$ 。
    • $r \le p$ 时,前面可以先用余数凑出来 $r$ 组 $mid + 1$ ,剩下来 $p - r$ 组 $mid$ 。考虑从 $p - r$ 组 $mid$ 中抽出来 $k$ 组 $mid$ 全拆成 $1$ 加到剩余的 $mid$ 上。肯定需要保证 $p - r - k \ge k \times mid$ ,保证剩下的 $mid$ 组数是大于 $k \times mid$ 的。即 $p - r \ge k \times (mid + 1)$ 。
    • 如果满足 $p - r \ge k \times (mid + 1)$ ,则需要分成 $p - k$ 组。不满足则需要分成 $p$ 组。
  2. 先凑成 $mid + 1$ ,然后考虑余数 $r, 0\le r \le mid$ 。如果余数不等于 $0$ ,将余数利用 $mid + 1$ 拆出来的 $1$ 凑成 $mid$ 。

代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
auto optimize_cpp_stdio = []()
{
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
std::cout.tie(nullptr);
return 0;
}();

class Solution
{
public:
const static int maxn = 1e5 + 10;
const static int maxm = 1e5 + 10;
const int INF = 0x3f3f3f3f;
int check(int mid, vector<int> &a)
{
// 每组都为 mid + 1,或 mid
int ret = 0;
for (auto &x : a)
{
int p = x / mid;
int r = x % mid;
if (p < r)
return -1;
// p >= r;
if ((p - r) >= (mid + 1))
{
ret += p - (p - r) / (mid + 1);
}
else
{
ret += p;
}
}
return ret;
}
int minGroupsForValidAssignment(vector<int> &nums)
{
unordered_map<int, int> cnt;
int minx = INF;
for (auto &x : nums)
{
cnt[x]++;
}
vector<int> a;
for (auto &[key, value] : cnt)
{
a.emplace_back(value);
minx = min(minx, value);
}
if (a.size() == 1)
return 1;
// 每组至少的数量
for (int i = minx; i >= 1; --i)
{
int ret = check(i, a);
if (ret != -1)
{
return ret;
}
}
return 1;
}
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
auto optimize_cpp_stdio = []()
{
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
std::cout.tie(nullptr);
return 0;
}();

class Solution
{
public:
const static int maxn = 1e5 + 10;
const static int maxm = 1e5 + 10;
const int INF = 0x3f3f3f3f;
int check(int mid, vector<int> &a)
{
// 每组都为 mid + 1,或 mid
int ret = 0;
for (auto &x : a)
{
int p = x / mid;
int r = x % mid;
if (p < r)
return -1;
// p >= r;
if ((p - r) >= (mid + 1))
{
ret += p - (p - r) / (mid + 1);
}
else
{
ret += p;
}
}
return ret;
}

int check2(int mid, vector<int> &a)
{
int ret = 0;
for (auto &x : a)
{
int p = x / (mid + 1);
int r = x % (mid + 1);
if(r == 0)
ret += p;
else
{
if (p < mid - r)
return -1;
ret += p + 1;
}

}
return ret;
}
int minGroupsForValidAssignment(vector<int> &nums)
{
unordered_map<int, int> cnt;
int minx = INF;
for (auto &x : nums)
{
cnt[x]++;
}
vector<int> a;
for (auto &[key, value] : cnt)
{
a.emplace_back(value);
minx = min(minx, value);
}
if (a.size() == 1)
return 1;
// 每组至少的数量
for (int i = minx; i >= 1; --i)
{
int ret = check2(i, a);
if (ret != -1)
{
return ret;
}
}
return 1;
}
};