原题:https://pintia.cn/problem-sets/994805342720868352/problems/994805409175420928
题目大意
给出多个集合元素,求任意两个集合的相似度,即交并比:两个集合的交集元素个数/两个集合的并集元素个数。
方法1(暴力模拟+最后一个测试点超时)
由于C++中Set数据结构即为我们常见的集合,满足不重复,且会自动排序,底部存储为红黑树
,
所以针对两个集合A
和B
,我们新开一个集合C
将两个集合的元素都插入到C
,则C就是我们的并集,最后求交集的元素个素只需要利用公式A + B - (A U B)
,即两个集合的元素减去两个集合并集的元素就是重复的元素。
// 最后一个测试点可能超时
#include<set>
#include<vector>
#include<iostream>
using namespace std;
vector<set<int> > S; // 集合列表
int main() {
int n, m, k, num;
scanf("%d", &n); S.resize(n);
for (int i = 0; i < n; i++) {
scanf("%d", &m);
for (int j = 0; j < m; j++) {
scanf("%d", &num);
S[i].insert(num);
}
}
int s1, s2;
scanf("%d", &k);
for (int i = 0; i < k; i++) {
scanf("%d %d", &s1, &s2);
set<int> tmp; // 表示两个集合的并集
for (auto iter = S[s1 - 1].begin(); iter != S[s1 - 1].end(); iter++) tmp.insert(*iter);
for (auto iter = S[s2 - 1].begin(); iter != S[s2 - 1].end(); iter++) tmp.insert(*iter);
int size1 = S[s1 - 1].size(), size2 = S[s2 - 1].size(), unionsize = tmp.size();
double rate = (size1 + size2 - unionsize) / (double)unionsize;
printf("%.1lf%%\n", rate * 100);
}
system("pause");
return 0;
}
复杂度分析,红黑树插入一个元素的复杂度在
O
(
l
o
g
n
)
O(logn)
O(logn),则插入n歌元素的复杂度在
O
(
n
l
o
g
n
)
O(nlogn)
O(nlogn),遍历两个集合新建集合的复杂度大概在
O
(
2
n
l
o
g
n
)
O(2nlogn)
O(2nlogn),可能会造成最后一个测试点超时
多次提交通过后,第4测试点时间在478ms,与500ms限制非常接近。
小优化
由于红黑树的插入和查找复杂度均在
O
(
l
o
g
n
)
O(logn)
O(logn),我们初始化nc = 0, nt = B.size()
所以我们可以遍历集合A
,然后查找该元素在B
中是否存在,若存在,则nc++
,否则nt++
,复杂度大概在
O
(
n
l
o
g
n
)
O(nlogn)
O(nlogn),减少了一半。
// 不新建立集合,而是在原集合上遍历
#include<set>
#include<vector>
#include<iostream>
using namespace std;
vector<set<int> > S; // 集合列表
int main() {
int DEBUG = 0;
int n, m, k, num;
scanf("%d", &n); S.resize(n);
for (int i = 0; i < n; i++) {
scanf("%d", &m);
for (int j = 0; j < m; j++) {
scanf("%d", &num);
S[i].insert(num);
}
}
int s1, s2;
scanf("%d", &k);
for (int i = 0; i < k; i++) {
scanf("%d %d", &s1, &s2);
set<int> tmp; // 表示两个集合的并集
int nc = 0, nt = S[s2-1].size(); // 交集和并集元素个数
for (auto iter = S[s1 - 1].begin(); iter != S[s1 - 1].end(); iter++) {
if (S[s2 - 1].find(*iter) != S[s2 - 1].end()) nc++; // 都有
else nt++; //一个有
}
printf("%.1f%%\n", (nc / (float)nt) * 100);
}
system("pause");
return 0;
}
可以看到测试点4时间大大减少。