蓝桥杯备考---集合

并查集

        入门题p1551 亲戚 详情见洛谷

ac代码

#include<iostream>
using namespace std;//针对多个集合确定每个集合的代表 通过代表判断两个元素是否在同一集合中 ---> 并查集 
int relative[5010];//第i个人亲戚的代表 
int n, m, p;
void init(){//最初 每个人都是自己这一个集合的代表 
	for(int i = 1; i <= n; i ++)
		relative[i] = i;
}
int find(int x){//查找该元素的代表 
	if(x == relative[x])	return x;
	else return find(relative[x]);
}
void Conform(int a, int b){//确定两个元素的代表 
	int x = find(a), y = find(b);//找出各自代表
	//若两个元素代表不一致 
	if(x != y) relative[y] = x;//将其中一方的代表设置为另一方集合的代表 合并两个集合 
}
int main(){
	cin >> n >> m >> p;
	init();
	 
	int a, b;
	for(int i = 0; i < m; i ++){
		cin >> a >> b;
		Conform(a, b);
	} 
	
	while(p --){
		cin >> a >> b;
		if(find(a) == find(b))
			cout << "Yes" << endl;
		else
			cout << "No" << endl;
	}
}

存在的问题

模板优化

// 一定不要忘了初始化,每个元素单独属于一个集合
void init() {
    for (int i = 1; i <= n; i++)
    f[i] = i;
}
int find(int x) { // 查询集合的“代表”
    if (x == fa[x])return x;
    return fa[x] = find(fa[x]); // 顺便【路径压缩】 
    // 这里写的很巧妙 返回值与赋值语句加在一起 使以后的递归次数降为1
}
void join(int c1, int c2) { // 合并两个集合
// f1为c1的代表,f2为c2的代表
    int f1 = find(c1), f2 = find(c2);
    if (f1 != f2) {
        if (size[f1] < size[f2]) // 【取较大者】作为代表
            swap(f1, f2);
        fa[f2] = f1;
        size[f1] += size[f2]; // 只有“代表”的size是有效的
    }
}

哈希表

        入门题p3370---针对多个字符串 统计不同的个数 采用计数排序的思想 + 哈希表的数据结构

计数排序:对于给定的一组数据 开辟额外的数组记录该数在序列中出现的次数 flag[3] = 2 代表3在序列中出现了两次 遍历完数组后 再去遍历开辟的记录数组 组值>0代表出现过 依次输出组值不为0的即可得到有序序列

从计数排序可以看出额外的数组下标就是当前数据的值 或经过映射后的值  若数据过大 考虑使用映射的方法 比如经典的取模运算 取模运算保证得到非负数 满足下标要求 121 % 11 = 0  hash[0] = 121

由此又产生了问题 随着取模范围的大小变化  会产生或多或少的哈希冲突(死去的数据结构对你说了句hello world) 比如 22 % 11 = 0 hash[0] = 11

解决哈希冲突

1. 设计一个复杂的哈希函数 降低重复的可能 但不能完全避免     % 11 太过简单

2. 十字链表法 --- 彻底解决问题 利用链表或二维数组 倾向于二维数组 设计简单 比如 % 11 若结果一样 将这些结果依次放到对应的一维数组中  查找时时间复杂度会取决于冲突次数 效率偏低

参考代码

vector<long long> hash[maxh];
// 以下是插入集合的方式,查找也是类似
void int insert(x){
    int h = f(x); //计算哈希值
    for (int i == 0, sz=hash[h].size(); i<sz; i++)
        if (x == hash[h][i]) // 从数组中找到了这一项
            return; // 什么都不做退出
    hash[h].push_back(x); // 插入这个元素
}

3. 多哈希方法 采用二维的哈希数组 设计两个哈希函数 比如 % 11 % 5 得到的余数 分别作为行列下标 冲突概率会大幅降低 

哈希函数设计

ac代码

#include<iostream>
#include<vector>
using namespace std;
const int N = 1e5;
int base = 261;
int mod = 23333;//额外开辟的数组下标范围就在 0 ~ mod - 1  大小为 mod 
vector<string> link[N];//额外开辟的数组 下标为哈希函数计算结果 值为输入元素 
int ans;
void insert(string s){
	long long hash = 0;
	for(int i = 0; s[i]; i ++)//计算哈希值 进行映射
		hash = (hash * base + s[i]) % mod; 
	int length = link[hash].size();
	for(int i = 0; i < length; i ++)//用二维数组模拟十字链表 
		if(s == link[hash][i])	return;
	link[hash].push_back(s);
	ans ++;
}
int main(){
	int n;
	cin >> n;
	
	string s;
	while(n --){
		cin >> s;
		insert(s);
	}
	
	cout << ans;
	return 0;
}

 理念介绍完毕 真神登场

特别注意 s.end()返回末尾哨兵的迭代器 其值为当前集合的长度

               s.find(x)在找不到元素时会返回s.end() 千万不要用 x == *s.find(x)做判断 有可能当前集合的长度正好等于你要找的元素值 血的教训--->调了我半个小时呀 我说这哪有bug呀

补充 s.insert(x) 在集合中插入x 它的返回值为pair<该值的迭代器,是否插入成功>

这里的插入成功是指该值第一次插入 若之前集合中存在 则返回false  对于某些题可能会容易解决一些  这道题也可以使用 代替插入失败的判断条件

      洛谷p5250 利用集合特性解决问题  详情见专页

注意:又是血的教训(大喜)m.insert({a, b}) != m[a] = b;

区别在于若m[a]已经存在值 第一种方法不会改变原来的值 而第二种会覆盖掉之前的值

        洛谷p5266 入门题 熟悉相关函数操作

迭代器介绍:C++迭代器_Zhc_AuC的博客-CSDN博客 比较详细

关于O2优化:C++手动开启O2优化(以及-O -O1 -O2 -O3优化的知识点)(竞赛可用)_c++ o2优化 了什么?_xuanweiace的博客-CSDN博客

蓝桥杯是中国最具知名度和影响力的计算机竞赛,每年举办一次国赛和一次省赛。备考蓝桥杯国赛,可以借助CSDN这个技术社区,收获许多宝贵的资源和经验。 首先,CSDN是一个集合了大量优质的计算机技术文章、博客和问答的平台。在CSDN上,我们可以找到许多与蓝桥杯相关的题目分析、解题思路以及优秀代码分享。这些资源可以帮助我们加深对蓝桥杯题目的理解,提升解题的能力。同时,通过阅读其他选手的解题思路和代码,我们可以学习到其他思维的启发,提高自己的编程水平。 其次,CSDN上有许多蓝桥杯的培训课程和学习资料。这些课程针对蓝桥杯的各个竞赛项目,从基础知识到高级技巧都有详细的讲解和练习。通过参加这些课程,我们可以系统地学习和巩固所需的知识点,增加自己对蓝桥杯各类问题的解决思路和方法的掌握程度。 此外,在CSDN论坛中,还有很多热心的蓝桥杯爱好者及往届选手积极分享经验和答疑解惑。我们可以在CSDN论坛上提问,和其他选手讨论交流,共同解决自己遇到的问题。通过和其他选手的互动,我们可以更好地认识到自己的不足之处,从而有针对性地提升自己的能力。 综上所述,备考蓝桥杯国赛期间,CSDN可以提供大量的学习资源,帮助我们深入理解题目、学习解题思路和提高编程实力。通过合理利用CSDN的各种功能和资源,我们可以更有针对性地备战蓝桥杯国赛,争取取得更好的成绩。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值