(PAT Advance)1063.Set Similarity (集合+交并比) C++

原题:https://pintia.cn/problem-sets/994805342720868352/problems/994805409175420928
在这里插入图片描述
题目大意

给出多个集合元素,求任意两个集合的相似度,即交并比:两个集合的交集元素个数/两个集合的并集元素个数。

方法1(暴力模拟+最后一个测试点超时)

由于C++中Set数据结构即为我们常见的集合,满足不重复,且会自动排序,底部存储为红黑树
所以针对两个集合AB,我们新开一个集合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时间大大减少。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值