《孟子·告子上》有名言:"鱼,我所欲也,熊掌,亦我所欲也;二者不可得兼,舍鱼而取熊掌者也。" 但这世界上还是有一些人可以做到鱼与熊掌兼得的。
给定 n 个人对 m 种物品的拥有关系。对其中任意一对物品种类(例如"鱼与熊掌"),请你统计有多少人能够兼得?
对每次查询,输出同时拥有两种物品的人数。
#include <iostream>
#include <vector>
#include <bitset>
#include <unordered_map>
using namespace std;
const int MAXM = 100000;
int main() {
int n, m;
scanf("%d %d", &n, &m);
// 使用 bitset 存储每个人的物品集合
vector<bitset<MAXM+1>> people(n);
// 读取每个人的物品清单
for (int i = 0; i < n; i++) {
int k;
scanf("%d", &k);
for (int j = 0; j < k; j++) {
int item;
scanf("%d", &item);
people[i].set(item); // 在对应位置设置为1
}
}
int q;
scanf("%d", &q);
// 使用哈希表缓存查询结果
unordered_map<long long, int> cache;
while (q--) {
int a, b;
scanf("%d %d", &a, &b);
// 生成唯一的缓存键
long long key = (long long)a * (m + 1) + b;
if (cache.count(key)) {
printf("%d\n", cache[key]);
continue;
}
// 计算同时拥有物品 a 和 b 的人数
int count = 0;
for (int i = 0; i < n; i++) {
if (people[i].test(a) && people[i].test(b)) {
count++;
}
}
// 缓存结果
cache[key] = count;
printf("%d\n", count);
}
return 0;
}
bitset<MAXM+1>:高效的位集合people[i].set(item):设置物品位people[i].test(a):检查物品位unordered_map:O(1) 查询缓存O(n × K)
读取并设置每个人的物品集合
O(Q × n) (最坏情况)
每次查询遍历所有人,有缓存优化
O(n × m/8)
bitset 紧凑存储,每位1bit
O(Q)
哈希表存储查询结果
4 8
3 4 1 8
4 7 1 8 4
5 6 5 1 2 3
4 3 2 4 8
3
2 3
7 6
8 4
2
0
3
| 人员 | 拥有物品 | 物品2 | 物品3 | 物品7 | 物品6 | 物品8 | 物品4 |
|---|---|---|---|---|---|---|---|
| 人员1 | {4, 1, 8} | ❌ | ❌ | ❌ | ❌ | ✅ | ✅ |
| 人员2 | {7, 1, 8, 4} | ❌ | ❌ | ✅ | ❌ | ✅ | ✅ |
| 人员3 | {6, 5, 1, 2, 3} | ✅ | ✅ | ❌ | ✅ | ❌ | ❌ |
| 人员4 | {3, 2, 4, 8} | ✅ | ✅ | ❌ | ❌ | ✅ | ✅ |
使用位运算替代集合操作,内存压缩8倍
哈希表存储查询结果,避免重复计算
bitset自动优化内存对齐和访问
多线程并行处理查询,提升吞吐量
预计算热门物品对的交集结果
将人员分块,利用局部性原理
| 方法 | 时间复杂度 | 空间复杂度 | 优缺点 |
|---|---|---|---|
| 朴素方法 | O(Q × n × K) | O(n × K) | 慢,内存大 |
| bitset方法 | O(Q × n) | O(n × m/8) | 快,内存优化 |
| bitset+缓存 | O(Q + unique × n) | O(n × m/8 + Q) | 最优解 |
本实验展示了如何通过合理的数据结构选择和算法优化,
将看似简单的问题转化为高效、优雅的解决方案。