|
| 1 | +# 使用 popcount 使得两个集合完全相等的最小操作次数 |
| 2 | + |
| 3 | +``` |
| 4 | +v2 |
| 5 | +created, 2025-08-17T15:56+08:00 |
| 6 | +published, 2025-08-17T16:27+08:00 |
| 7 | +category: algorithm-problems |
| 8 | +``` |
| 9 | + |
| 10 | +给定两个长度相同的数组 A 和 B,通过将元素转换为其二进制表示中 1 的个数(每次转换记为 1 次操作), |
| 11 | +计算使两个数组中不匹配的元素变得匹配所需的最少操作次数。 |
| 12 | + |
| 13 | +不需要考虑元素匹配的顺序。 |
| 14 | + |
| 15 | +```bash |
| 16 | +input: |
| 17 | + |
| 18 | +5 3 7 |
| 19 | +3 3 5 |
| 20 | + |
| 21 | +output: |
| 22 | +1 |
| 23 | +``` |
| 24 | + |
| 25 | +解释:直接将 A 中的 7 换成 3 即可,只需要 1 次变化,A 变换后为 `[5,3,3]`,B 是 `[3,3,5]`,题目说不需要考虑顺序,所以一次变换就够了。 |
| 26 | + |
| 27 | +这题是在美团一次机试中出的题目,有一种可以证明正确性的做法是,我们计算一个矩阵 M,`M[i][j]` 表示 `A[i]` 到 `B[j]` 所需变换次数。 |
| 28 | + |
| 29 | +然后这就是一个图的匹配问题了。 |
| 30 | + |
| 31 | +后来我想到一个贪心的算法: |
| 32 | +因为两个数组中,最大且唯一的的那个元素,比如 sample 中的是 7,只在 A 中出现。 |
| 33 | +最大的元素一定要被 popcount 的,所以我们把 7 popcount 掉,然后塞回 A。 |
| 34 | + |
| 35 | +直到 A 和 B 相等。 |
| 36 | + |
| 37 | +唯一的问题是,这个策略是最优的吗?不妨假设 A 中最大为 x,B 中最大为 y,`x == y`, |
| 38 | +假设最后 x 和 y 没有匹配上,最后的匹配结果是 x 经过 m 次匹配到了 B 中的 b, y 经过 n 次匹配到了 A 中的 a。 |
| 39 | + |
| 40 | +这样总共就是 n+m 次匹配。 |
| 41 | + |
| 42 | +如果直接让 x 和 y 配对,a 和 b 配对,最后 也只需要 `|n-m|` 次匹配。 |
| 43 | + |
| 44 | +所以 |
| 45 | + |
| 46 | +1. 如果数组 A 和 B 有两个相等的数,应该让他们直接配对,他俩可以直接从数组中移除掉 |
| 47 | +2. 对于 A U B 中最大的那个孤零零的数,一定要把它 popcount |
| 48 | + |
| 49 | +[美团 0816 秋招笔试真题解析](https://mp.weixin.qq.com/s/3ypuE6SUSUydwQVoi0w72Q) 评论区有人说种做法可以 ak。 |
| 50 | + |
| 51 | +附代码,简直是短小精悍: |
| 52 | + |
| 53 | +```cpp |
| 54 | +#include <bit> |
| 55 | +#include <cstdint> |
| 56 | +#include <iostream> |
| 57 | +#include <queue> |
| 58 | +using namespace std; |
| 59 | + |
| 60 | +int calCount(priority_queue<uint32_t> &a, priority_queue<uint32_t> &b) { |
| 61 | + int count = 0; |
| 62 | + while (!a.empty() && !b.empty()) { |
| 63 | + auto x = a.top(); |
| 64 | + auto y = b.top(); |
| 65 | + if (x == y) { |
| 66 | + a.pop(); |
| 67 | + b.pop(); |
| 68 | + } else if (x > y) { |
| 69 | + a.pop(); |
| 70 | + a.push(std::popcount(x)); |
| 71 | + count++; |
| 72 | + } else { |
| 73 | + b.pop(); |
| 74 | + b.push(std::popcount(y)); |
| 75 | + count++; |
| 76 | + } |
| 77 | + } |
| 78 | + return count; |
| 79 | +} |
| 80 | + |
| 81 | +int main() { |
| 82 | + int n; |
| 83 | + std::cin >> n; |
| 84 | + while (n-- > 0) { |
| 85 | + int len; |
| 86 | + cin >> len; |
| 87 | + priority_queue<uint32_t> a, b; |
| 88 | + for (int i = 0; i < len; i++) { |
| 89 | + uint32_t x; |
| 90 | + cin >> x; |
| 91 | + a.push(x); |
| 92 | + } |
| 93 | + for (int i = 0; i < len; i++) { |
| 94 | + uint32_t x; |
| 95 | + cin >> x; |
| 96 | + b.push(x); |
| 97 | + } |
| 98 | + cout << calCount(a, b) << endl; |
| 99 | + } |
| 100 | + return 0; |
| 101 | +} |
| 102 | +``` |
0 commit comments