贪心
解法:贪心
思维第一步
看到“任一元素加上或减去 value
”,而且“可以操作任意次”,马上反馈出等价条件:
数 $x$ 可以变成任意满足
y mod value == x mod value
的数 $y$。
因此,我们可以把整个序列按元素取模的值分成 value
类,每一类元素都不会因为操作而变成其他类别里的元素。
思维第二步
既然我们要让“缺失的最小非负整数”最大,设第 $i$ 类里有 $k$ 个数,那么它们需要变成 $i, v + i, 2v + i, \cdots (k - 1)v + i$ 才能让 mex 尽可能大。
举个例子帮助大家理解,假设 $v = 5$,第 $2$ 类里有 $3$ 个数,那么它们需要变成 $2, 7, 12$ 才能让 mex 尽可能大。如果变成别的,比如 $2, 12, 17$,那因为缺失了 $7$(而且其他类别的数也没法变成 $7$),那 mex 至多就是 $7$ 了。如果是 $2, 7, 12$,那 mex 还有可能是 $17$,肯定这样做更优。
最终处理
既然我们已经确定了每个数都要变成什么,那完成变化以后,直接计算变化后序列的 mex 就是答案。
复杂度 $\mathcal{O}(n)$。
参考代码(c++)
###c++
class Solution {
public:
int findSmallestInteger(vector<int>& nums, int v) {
int n = nums.size();
// vis[i] 表示变化后的序列里是否出现过元素 i
// 答案肯定不超过 n
// 因为最好情况就是 nums = [0, 1, ..., n - 1],这样 mex = n
bool vis[n + 1];
memset(vis, 0, sizeof(vis));
// cnt[i] 表示第 i 类元素目前有几个
int cnt[v];
memset(cnt, 0, sizeof(cnt));
for (int x : nums) {
// t 是元素 x 所属的类别
int t = (x % v + v) % v;
// y 是元素 x 应该变成什么数
int y = cnt[t] * v + t;
if (y < n) vis[y] = true;
cnt[t]++;
}
// 计算变化后序列的 mex
for (int i = 0; i <= n; i++) if (!vis[i]) return i;
return -1;
}
};