Skip to content

Commit 7df7ea1

Browse files
committed
feat: new popcount match
1 parent e6ca7a1 commit 7df7ea1

2 files changed

Lines changed: 104 additions & 0 deletions

File tree

SUMMARY.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
# SUMMARY
22

33
- [Home](./README.md)
4+
- [algorithm-problems](./SUMMARY.md)
5+
- [使用 popcount 使得两个集合完全相等的最小操作次数](./algorithm-problems/popcount-match.md)
46
- [c/cpp](./SUMMARY.md)
57
- [cpp 中的内存序](./languages/c-cpp/cpp-memory-order/cpp-memory-order.md)
68
- [C++模板](./languages/c-cpp/cpp-template/cpp-template.md)
Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
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

Comments
 (0)