Codeup Contest ID:100000597
题目描述
Given two sets of integers, the similarity of the sets is defined to be Nc/Nt*100%, where Nc is the number of distinct common numbers shared by the two sets, and Nt is the total number of distinct numbers in the two sets. Your job is to calculate the similarity of any given pair of sets.
输入
Each input file contains one test case. Each case first gives a positive integer N (<=50) which is the total number of sets. Then N lines follow, each gives a set with a positive M (<=104) and followed by M integers in the range [0, 109]. After the input of sets, a positive integer K (<=2000) is given, followed by K lines of queries. Each query gives a pair of set numbers (the sets are numbered from 1 to N). All the numbers in a line are separated by a space.
输出
For each query, print in one line the similarity of the sets, in the percentage form accurate up to 1 decimal place.
样例输入
3
3 99 87 101
4 87 101 5 87
7 99 101 18 5 135 18 99
2
1 2
1 3
样例输出
50.0%
33.3%
思路
题目的大概意思是让你求两个集合的相似度nc/nt×100%,nc是两个集合中共有元素的个数(如样例中的集合1和集合2的共有元素是87、101,因此nc=2),而nt是两个集合中所有不同元素的个数(还是拿样例来说,也就是把集合1和集合2整合一下,新的集合是5、87、99、101,因此nt=4)。
第一个思路当然是先用set存储输入的数据,然后再对set1的每个元素进行遍历,到set2去找是否有相同的,如果相同,则nc++,如果不同,则nt++(具体操作可以看柳神的PAT题解哈,我这里就没做了)。
因为以上这么做肯定是要两重for循环的,于是为了防止再一次超时,我这里采用了投机的办法:用了C++自带的集合取并集、交集和差集的算法。我们来回顾一下题目的求解过程,首先nc是共有元素的个数,那么无论是B-A还是A-B,得到的都是减去共有元素之后的集合(因为set自动去重并升序),比如取集合1和集合2的差集(1-2),得到的应该是{99},仅剩1个元素。
于是nc就应该等于原集合的元素数量减去现存的元素数量:nc = B.size()-(B-A).size()。
那么nt怎么得到呢,我们最直观的思路就是把第二个集合的元素全部添加到第一个集合里,让它自己去重并升序,得到的新集合的元素个数就是nt,其实这个操作就是取并集,所以nt = (A+B).size()。
为了方便存储运算后的集合,我这里用了vector来存放(用set、list也行,因为最后一个参数存放的是迭代器)。
下面简单介绍一下三个函数(并集、交集、差集)的用法,想要详细了解的请看博客:
关于C++里面使用set_union,set_intersection等函数的使用总结
STL中set求交集、并集、差集的方法
这三个函数的参数是一样的,共有五个参数,前两个是集合x1的头和尾(迭代器),紧接着的两个是集合x2的头和尾(迭代器),最后一个是存放运算结果的目标容器的迭代器(OutputIterator),至于最后存放结果的容器x是什么就无所谓了,vector也行,set也行。
set_union(x1.begin(), x1.end(), x2.begin(), x2.end(), inserter(x, x.end()))
set_intersection(x1.begin(), x1.end(), x2.begin(), x2.end(), inserter(x, x.end()))
set_difference(x1.begin(), x1.end(), x2.begin(), x2.end(), inserter(x, x.end()))
最后想说的一点是,虽然这种投机的方法能AC,但是耗时也是很严重的:
如果超时的话还请选择用大佬们的方法来做。
代码
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
using namespace std;
set<int> mysets[51];//因为集合是从1~N命名的,所以要多开一位
vector<int> temp;
set<int>::iterator it;
int main(){
int N;
while(scanf("%d", &N) != EOF){
for(int i=1;i<=N;i++){
int cnt;
scanf("%d", &cnt);
for(int j=1;j<=cnt;j++){
int temp;
scanf("%d", &temp);
mysets[i].insert(temp);//集合的添加元素不是push_back
}
}
int K;
scanf("%d", &K);
for(int i=1;i<=K;i++){
int x, y;
scanf("%d%d", &x, &y);
int cnt = mysets[x].size();
set_difference(mysets[x].begin(), mysets[x].end(), mysets[y].begin(), mysets[y].end(), inserter(temp, temp.end()));
cnt -= temp.size();
temp.clear();
set_union(mysets[x].begin(), mysets[x].end(), mysets[y].begin(), mysets[y].end(), inserter(temp, temp.end()));
int total = temp.size();
temp.clear();
printf("%.1f%\n", (double)cnt/total*100);
}
}
return 0;
}
总的来说,set和vector比较相似,区别的地方主要在于set只能用迭代器访问元素,而vector可以用下标和迭代器,以及set自带去重和升序的功能。另外,看了柳神的PAT题解之后我才知道,原来vector