第9章 哈希表
1048. Find Coins
笔记
- 哈希表存储
- 每读入一个数
x
时,在哈希表中查找target - x
是否存在,如果存在则检查是否需要更新全局最优解
#include <iostream>
#include <unordered_set>
using namespace std;
const int INF = 1010;
unordered_set<int> h;
int main() {
int n, target;
cin >> n >> target;
int x, p = INF, q = INF;
while(n--) {
cin >> x;
int y = target - x;
if (h.count(y)) {
if (x > y) swap(x, y);
if (x < p) {
p = x;
q = y;
}
}
h.insert(x);
}
if (p == INF) puts("No Solution");
else cout << p << ' ' << q << endl;
return 0;
}
1063. Set Similarity
笔记
- 用哈希表存储集合$A$和$B$
- 在求集合$A \cap B$的元素个数时,可先遍历集合$A$的元素,如果该元素在集合$B$中存在,则计数+1,时间复杂度是$O(n+m)$,而$A \cup B$的元素个数时可根据公式在$O(1)$时间复杂度计算出
#include <iostream>
#include <unordered_set>
using namespace std;
const int N = 52;
unordered_set<int> s[N];
int main() {
int n, m, x;
scanf("%d", &n);
for (int i = 1; i <= n; i++) {
scanf("%d", &m);
while(m--) {
scanf("%d", &x);
s[i].insert(x);
}
}
int q, a, b;
scanf("%d", &q);
while(q--) {
scanf("%d%d", &a, &b);
int cnt = 0;
for (int x : s[a])
if (s[b].count(x))
cnt++;
double res = cnt * 100.0 / (s[a].size() + s[b].size() - cnt);
printf("%.1lf%%\n", res);
}
return 0;
}
1120. Friend Numbers
笔记
- 用集合
set
存储就行,不用统计有多少次 - 遍历
set
时,默认是升序遍历
#include <iostream>
#include <set>
using namespace std;
set<int> s;
int main() {
int n, x;
cin >> n;
while(n--) {
cin >> x;
int sum = 0;
while(x) {
sum += x % 10;
x /= 10;
}
s.insert(sum);
}
cout << s.size() << endl;
string res;
for (auto elem : s)
res += to_string(elem) + ' ';
res.pop_back();
cout << res << endl;
return 0;
}
1144. The Missing Number
笔记
- 插入到哈希表,然后再依次查询
#include <iostream>
#include <unordered_set>
using namespace std;
unordered_set<int> s;
int main() {
int n, x;
cin >> n;
while(n--) {
cin >> x;
s.insert(x);
}
int k = 1;
while(true) {
if (!s.count(k)) {
cout << k << endl;
break;
}
k++;
}
return 0;
}
笔记
#include <iostream>
#include <unordered_set>
using namespace std;
const int N = 1e4 + 10;
int a[N][2];
int main() {
int n, m;
cin >> n >> m;
for (int i = 0; i < n; i++)
cin >> a[i][0] >> a[i][1];
int x;
unordered_set<int> s;
while(m--) {
s.clear();
int t;
cin >> t;
while(t--) {
cin >> x;
s.insert(x);
}
bool success = true;
for (int i = 0; i < n; i++)
if (s.count(a[i][0]) && s.count(a[i][1])) {
success = false;
break;
}
if (success) puts("Yes");
else puts("No");
}
return 0;
}
1149. Dangerous Goods Packaging
笔记
- 先存储所有危险的组合,然后读入集装箱的物品id,遍历所有危险组合,如果每一种组合的两个元素同时再集装箱中出现,则危险
#include <iostream>
#include <unordered_set>
using namespace std;
const int N = 1e4 + 10;
int a[N][2];
int main() {
int n, m;
cin >> n >> m;
for (int i = 0; i < n; i++)
cin >> a[i][0] >> a[i][1];
int x;
unordered_set<int> s;
while(m--) {
s.clear();
int t;
cin >> t;
while(t--) {
cin >> x;
s.insert(x);
}
bool success = true;
for (int i = 0; i < n; i++)
if (s.count(a[i][0]) && s.count(a[i][1])) {
success = false;
break;
}
if (success) puts("Yes");
else puts("No");
}
return 0;
}
1078. Hashing
笔记
- 模拟二次探测法(把哈希表看做循环数组)
- 注意把长度不是质数的哈希表长度变成质数长度
#include <iostream>
using namespace std;
const int N = 1e4 + 10;
int m, n, h[N];
bool is_prime(int x) {
if (x < 2) return false;
for (int i = 2; i <= x / i; i++)
if (x % i == 0)
return false;
return true;
}
int get_pos(int x) {
int t = x % m;
for (int k = 0; k < m; k++) {
int p = (t + k * k) % m; // 循环数组
if (!h[p]) return p;
}
return -1; // 无法插入
}
int main() {
cin >> m >> n;
while (!is_prime(m)) m++; // 修正成质数
int x;
string res;
while(n--) {
cin >> x;
int t = get_pos(x);
if (t == -1) res += "- ";
else {
h[t] = x;
res += to_string(t) + ' ';
}
}
res.pop_back();
cout << res << endl;
return 0;
}
1137. Final Grading
笔记
- 学生信息是结构体,但由于名字是字符串,用哈希表
unordered_map<string, Student>
可以加快查找效率
#include <iostream>
#include <cstring>
#include <unordered_map>
#include <algorithm>
#include <vector>
#include <cmath>
using namespace std;
const int N = 1e4 + 10, M = 21;
char id[M];
struct Student {
string id;
int gp, gm, gf, g;
Student(): gp(-1), gm(-1), gf(-1), g(0) {};
bool operator< (const Student& s) const {
if (g != s.g) return g > s.g;
else return id < s.id;
}
};
unordered_map<string, Student> students;
int main() {
int p, m, n;
scanf("%d%d%d", &p, &m, &n);
int score;
for (int i = 0; i < p; i++) {
scanf("%s%d", id, &score);
students[id].gp = score;
students[id].id = id;
}
for (int i = 0; i < m; i++) {
scanf("%s%d", id, &score);
students[id].gm = score;
students[id].id = id;
}
for (int i = 0; i < n; i++) {
scanf("%s%d", id, &score);
students[id].gf = score;
students[id].id = id;
}
vector<Student> res;
for (auto item : students) {
Student stu = item.second;
if (stu.gf > stu.gm) stu.g = stu.gf;
else stu.g = round(stu.gf * 0.6 + stu.gm * 0.4);
if (stu.gp >= 200 && stu.g >= 60)
res.push_back(stu);
}
sort(res.begin(), res.end());
for (auto s : res)
cout << s.id << ' ' << s.gp << ' ' << s.gm << ' ' << s.gf << ' ' << s.g << endl;
return 0;
}
1145. Hashing - Average Search Time
笔记
- 模拟哈希表,采用二次探测法解决冲突,需要计算平均查询次数
- 当查到该元素或发现可用的空位时,查询次数等于进入循环的次数
- 当查不到该元素时,查询次数等于哈希表容量+1,依旧等价于进入循环的次数
#include <iostream>
using namespace std;
const int N = 1e4 + 10;
int m, n, q, h[N];
bool is_prime(int x) {
if (x < 2) return false;
for (int i = 2; i <= x / i; i++)
if (x % i == 0)
return false;
return true;
}
int get_pos(int x, int& cnt) {
cnt = 1; // 进入循环前就认为查询一次
int t = x % m;
for (int k = 0; k < m; k++) {
int p = (t + k * k) % m;
if (!h[p] || h[p] == x)
return p;
cnt++;
}
return -1; // 全都查不到时,返回cnt == m + 1
}
int main() {
cin >> m >> n >> q;
while(!is_prime(m)) m++;
int cnt, t, x;
for (int i = 0; i < n; i++) {
cin >> x;
t = get_pos(x, cnt);
if (t == -1) printf("%d cannot be inserted.\n", x);
else h[t] = x;
}
int sum = 0;
for (int i = 0; i < q; i++) {
cin >> x;
get_pos(x, cnt);
sum += cnt;
}
printf("%.1lf\n", (double) sum / q);
return 0;
}