如何求「加减 value 任意次后的最大 MEX」同余类求解
算法解析:如何求「加减 value 任意次后的最大 MEX」
前言
最近在刷题时遇到一道有趣的题目:
给你一个整数数组
nums
和一个整数value
,你可以对数组中的任意元素做任意次加上或减去value
的操作。
问:执行任意操作后,数组的 最大 MEX(最小缺失非负整数)是多少?
乍一看,这题和普通 MEX 题不一样,它引入了“±value 操作”,需要一点数学和余数类的知识才能高效解决。
一、理解题目
关键点:
- 每个数可以 ±
value
任意次。 - MEX(Minimum EXcluded)是数组中缺失的最小非负整数。
- 我们想找到 在操作任意次后,数组能覆盖的最大连续非负整数序列。
二、核心思想:同余类
1. 同余类定义
给定正整数 m
,两个整数 a
和 b
如果满足:
a ≡ b (mod m)
也就是 (a - b)
可以被 m
整除,那么 a
和 b
属于同一 同余类。
同余类 [a]_m
定义为:
[a]_m = { a + k*m | k ∈ Z }
也就是说,同余类里的每个数模 m
后余数相同。
2. 为什么本题与同余类相关?
题目允许 ±value
操作:
num → num + k*value
那么每个数 只能生成它所在同余类中的数:
- 余数 =
(num % value + value) % value
- 类内数值可正可负,形如
num + k*value
例如:
num = 2, value = 3
可生成数 = ..., -4, -1, 2, 5, 8, ...
所有数模 3 = 2
三、算法步骤
1. 统计每个同余类数量
int[] count = new int[value];
for (int num : nums) {int r = ((num % value) + value) % value;count[r]++;
}
count[r]
表示余数类r
中的元素数量- 数组长度 =
value
,因为余数类编号[0, value-1]
2. 贪心计算最大 MEX
从 mex = 0
开始:
-
计算
mod = mex % value
-
检查
count[mod] > 0
- 如果有 → 用掉一个 →
mex++
- 如果没有 → 当前
mex
就是最大 MEX
- 如果有 → 用掉一个 →
int mex = 0;
while (true) {int mod = mex % value;if (count[mod] > 0) {count[mod]--;mex++;} else {break;}
}
return mex;
四、示例讲解
nums = [1, -10, 7, 13, 6, 8]
value = 5
1. 计算同余类
num | mod = ((num%5)+5)%5 | 类 |
---|---|---|
1 | 1 | 1 |
-10 | 0 | 0 |
7 | 2 | 2 |
13 | 3 | 3 |
6 | 1 | 1 |
8 | 3 | 3 |
统计:
count = [1, 2, 1, 2, 0]
2. 贪心生成 MEX
mex | mex%5 | count[mex%5] | 操作结果 |
---|---|---|---|
0 | 0 | 1 | 用掉 → count[0]=0 → mex++=1 |
1 | 1 | 2 | 用掉 → count[1]=1 → mex++=2 |
2 | 2 | 1 | 用掉 → count[2]=0 → mex++=3 |
3 | 3 | 2 | 用掉 → count[3]=1 → mex++=4 |
4 | 4 | 0 | 没有 → 停止 |
最大 MEX = 4
五、时间复杂度
- 遍历数组统计同余类:
O(n)
- 贪心计算 MEX:每次最多
O(n)
次(因为每个元素最多用一次) - 总复杂度:
O(n)
空间复杂度:O(value)
六、总结
- 同余类是解决这类题目的关键:±value 的操作只改变数值,不改变余数类。
- 贪心 MEX:每次用掉余数类里一个元素生成对应的非负整数。
- 公式
(num % value + value) % value
保证余数非负,防止负数取模出错。 - 算法高效、易于实现。
🔗 参考代码(Java)
public int findSmallestInteger(int[] nums, int value) {int[] count = new int[value];for (int num : nums) {int r = ((num % value) + value) % value;count[r]++;}int mex = 0;while (true) {int mod = mex % value;if (count[mod] > 0) {count[mod]--;mex++;} else {break;}}return mex;
}