一道STL(set)的练习题。
set翻译为集合,是一个内部自动有序且不含重复元素的容器,这是set非常重要的特性,要在实际问题中充分利用之。
除此之外,值得注意的是,set的基本操作:insert()、find()等一些操作的时间复杂度为O(logN)。
题目链接在此。
题意
给出N个集合,然后有K个查询,给出每个查询的Set Similarity,即(Nc/Nt)*100.0。Nc为两个集合的交集的元素个数,Nt为两个集合的并集的元素个数。
思路及实现
刚开始有两种想法:
- 想法一:对于交集,自己实现一个求交集的函数,时间复杂度到了O(N^2);对于并集,则利用set的特性,将两个集合放到一个set中,即可得到并集。
- 想法二:直接利用< algorithm >头文件中的set_union()、set_intersection()求得并集和交集
实践证明,以上两种方法都会有一组测试点超时。
那么如何降低求交集和并集的时间复杂度呢?想想上面提到的set的find()操作,它的时间复杂度达到了O(logN)。
以下是程序的正确思路,时间复杂度为O(NlogN):
- 用一个set数组sets保存所有输入的集合
- 要查询的两个集合分别为sets[a]、sets[b]
- 初始化Nc=0, Nt=sets[b].size()(集合b的元素个数)
- 然后一边遍历集合a,一边用find()操作在集合b中查找是否在b中也存在某元素,若存在,则Nc++,若不存在,则Nt++。最后即可求得所求比值。
#include<stdio.h>
#include<set>
#include<algorithm>
using namespace std;
const int N = 51;
set<int> sets[N];
void getNcAndNt(int a, int b, int &nc, int &nt){ //求集合a与集合b的nc和nt
for(set<int>::iterator ita = sets[a-1].begin(); ita != sets[a-1].end(); ita++){
if(sets[b-1].find(*ita) != sets[b-1].end()){ //在b集合中能找到该元素
nc++;
}else{ //在b集合中不能找到该元素
nt++;
}
}
}
int main(){
int n; // total number of set
scanf("%d",&n);
for(int i = 0; i < n; i++){ //get sets
int num;
scanf("%d", &num);
for(int j = 0; j < num; j++){
int temp;
scanf("%d", &temp);
sets[i].insert(temp);
}
}
//获取查询并求并集和交集个数
int query;
scanf("%d", &query);
for(int i = 0; i < query; i++){
int a,b;
scanf("%d %d",&a, &b);
int nc = 0,nt = sets[b-1].size(); //交集个数,并集个数
getNcAndNt(a, b, nc, nt);
printf("%.1lf%%\n", nc*100.0/nt);
}
return 0;
}