1.并查集【模板】
- 找到祖先是谁
int find(int x) {//查找x的族长 if (x == fa[x]) return x; return fa[x] = find(fa[x]);//把向下寻找的结点定为新的族长 }
- 两个集合相并
void join(int x, int y) {//并到同一集合中区【模板函数】 int f1 = find(x); int f2 = find(y); if (f1 != f2) { fa[f1] = f2;//插入f2集合中去。 } }
2.哈希表建立
哈希表的公式由第四章介绍。
重点:
- 关于模的概念(可先看深入浅出上的数字不同次数计算了解)
- hash公式
- 字符串哈希表注意转换hash码
例题:P3370 【模板】字符串哈希
题目描述
如题,给定 NN 个字符串(第 ii 个字符串长度为 M_iMi,字符串内包含数字、大小写字母,大小写敏感),请求出 NN 个字符串中共有多少个不同的字符串。
友情提醒:如果真的想好好练习哈希的话,请自觉,否则请右转PJ试炼场:)
输入格式
第一行包含一个整数 NN,为字符串的个数。
接下来 NN 行每行包含一个字符串,为所提供的字符串。
输出格式
输出包含一行,包含一个整数,为不同的字符串个数。
输入输出样例
输入 #1复制
5 abc aaaa abc abcc 12345输出 #1复制
4说明/提示
对于 30\%30% 的数据:N\leq 10N≤10,M_i≈6Mi≈6,Mmax\leq 15Mmax≤15。
对于 70\%70% 的数据:N\leq 1000N≤1000,M_i≈100Mi≈100,Mmax\leq 150Mmax≤150。
对于 100\%100% 的数据:N\leq 10000N≤10000,M_i≈1000Mi≈1000,Mmax\leq 1500Mmax≤1500。
样例说明:
样例中第一个字符串(abc)和第三个字符串(abc)是一样的,所以所提供字符串的集合为{aaaa,abc,abcc,12345},故共计4个不同的字符串。
#include <iostream> #include <vector> #include <string> #define mod 23333 #define k 261 using namespace std; char s[1520]; vector<string>link[mod + 2]; int n, ans=0; inline void insert() { int hash = 1; for (int i = 0; s[i]; i++) hash = (hash*1ll*k + s[i]) % mod; string t = s; for (int i = 0; i < link[hash].size(); i++) { if (link[hash][i] == t) {//是否能搜索到该字符串 return; } } link[hash].push_back(t);//搜索不到,则加入该数组内 ans++;//不同字符串次数+1 } int main() { cin >> n; for (int i = 1; i <= n; i++) { cin >> s, insert(); } cout << ans << endl; }
3.set
set ,重点!!!
其实关于set的增加,删除,查找一类与其他函数的使用方法差不多的,关键一点在于:
s.lower_bound(x)//返回不大于x的第一个元素地址 s.upper_bound()//返回大于x的第一个元素地址
后续内容会增加(据说set十分有用)
例题:P5250 【深基17.例5】木材仓库
题目描述
博艾市有一个木材仓库,里面可以存储各种长度的木材,但是保证没有两个木材的长度是相同的。作为仓库负责人,你有时候会进货,有时候会出货,因此需要维护这个库存。有不超过 100000 条的操作:
- 进货,格式
1 Length
:在仓库中放入一根长度为 Length(不超过 10^9109) 的木材。如果已经有相同长度的木材那么输出Already Exist
。- 出货,格式
2 Length
:从仓库中取出长度为 Length 的木材。如果没有刚好长度的木材,取出仓库中存在的和要求长度最接近的木材。如果有多根木材符合要求,取出比较短的一根。输出取出的木材长度。如果仓库是空的,输出Empty
。输入格式
无
输出格式
无
输入输出样例
输入 #1复制
7 1 1 1 5 1 3 2 3 2 3 2 3 2 3输出 #1复制
3 1 5 Empty#include <iostream> #include <set> using namespace std; int n,m,lenth; set<int>s; int main() { cin >> n; while (n--) { cin >> m >> lenth; if (m == 1) { if (s.find(lenth) != s.end()) { cout << "Already Exist" << endl; } else { s.insert(lenth); } } else if (s.empty()) { cout << "Empty" << endl; } else { set<int>::iterator i = s.lower_bound(lenth), j = i;//这就是传说中的迭代器吧,嘿哈 if (j != s.begin())j--; if (i != s.end() && lenth - (*j) > (*i) - lenth) {//如果比它小的距离>比它大的距离,则用j代替i j = i; } cout << *j << endl; s.erase(j);//erase删除的是地址 } } }
迭代器的那个点还是要多注意!!!
4.map(yyds!!!)
曾经年少无知的我写的通讯管理系统几百行,没想到用map几十行就能实现,还是当年的我太天真了吧,哎!!!
不说了,代码装B:
#include<iostream> using namespace std; #include<string> #define MAX 1000//宏的定义 struct Person { string m_Name; int m_Sex; int m_Age; string m_Phone; string m_Addr; }; struct Addressbooks { struct Person personArray[MAX];//联系人数组,信息就储存在里面 int m_size;//联系人个数 }; void showMenu() { cout << "**************************" << endl; cout << "*******1.添加联系人*******" << endl; cout << "*******2.显示联系人*******" << endl; cout << "*******3.删除联系人*******" << endl; cout << "*******4.查找联系人*******" << endl; cout << "*******5.修改联系人*******" << endl; cout << "*******6.清空联系人*******" << endl; cout << "*******0.推出通讯录*******" << endl; cout << "**************************" << endl; } void addPerson(Addressbooks* abs) { //第一个功能添加联系人 if (abs->m_size == MAX) { cout << "通讯录已满,无法添加人了" << endl; return; } else { string name; cout << "请输入您的姓名:" << endl; cin >> name; abs->personArray[abs->m_size].m_Name = name; cout << "请输入您的性别" << endl; cout << "1----男" << endl; cout << "2----女 " << endl; int sex=0; while (true) { //注意观察这个循环 cin >> sex; if (sex == 1 || sex == 2) { abs->personArray[abs->m_size].m_Sex = sex; break; } cout << "您输入的性别有误,请您重新输入" << endl; } cout << "请输入您的年龄: " << endl; int age = 0; cin >> age; abs->personArray[abs->m_size].m_Age = age; cout << "请输入您的联系电话: " << endl; string phone; cin >> phone; abs->personArray[abs->m_size].m_Phone = phone; cout << "请输入您的家庭地址: " << endl; string address; cin >> address; abs->personArray[abs->m_size].m_Addr = address; abs->m_size++;//联系人个数增加一个。 } system("pause"); system("cls"); } //显示所有的联系人 void showPerson(Addressbooks* abs) { if (abs->m_size == 0) { cout << "通讯录里面还没有人呢" << endl; } else { for (int i = 0; i < abs->m_size; i++) { //在结构体数组当中一定要注意注意第一个开始是a[0]啊循环要从i=0开始 cout << "姓名:" << abs->personArray[i].m_Name << "\t"; cout << "性别:" << (abs->personArray[i].m_Sex == 1 ? "男" : "女") << "\t"; cout << "年龄:" << abs->personArray[i].m_Age << "\t"; cout << "联系电话:" << abs->personArray[i].m_Phone << "\t"; cout << "家庭住址:" << abs->personArray[i].m_Addr << endl; } } system("pause"); system("cls"); } int isExist(Addressbooks* abs, string name) { for (int i = 0; i < abs->m_size; i++) { if (abs->personArray[i].m_Name == name) { return i; } } return -1; } //第三个模块开始了 void deletePerson(Addressbooks * abs) { cout << "请输入您要删除的联系人" << endl; string name; cin >> name; int ret = isExist(abs,name); if (ret != -1) { for (int i = ret; i < abs->m_size; i++) { abs->personArray[i] = abs->personArray[i + 1]; } abs->m_size--; cout << "该联系人已删除成功" << endl; } else { cout << "查无此人" << endl; } system("pause"); system("cls"); } //第四个模块 void findPerson(Addressbooks* abs) { cout << "请输入您要查找的联系人:" << endl; string name; cin >> name; int ret = isExist(abs, name); if (ret != -1) { cout << "姓名:" << abs->personArray[ret].m_Name << "\t"; cout << "性别:" << (abs->personArray[ret].m_Sex == 1 ? "男" : "女") << "\t"; cout << "年龄:" << abs->personArray[ret].m_Age << "\t"; cout << "联系电话:" << abs->personArray[ret].m_Phone << "\t"; cout << "家庭住址:" << abs->personArray[ret].m_Addr << endl; } else { cout << "没有查到该人" << endl; } system("pause"); system("cls"); } //第五个模块开始了!!! void modifyPerson(Addressbooks* abs) { cout << "请输入您要修改的联系人的姓名:" << endl; string name; cin >> name; int ret = isExist(abs, name); if (ret != -1) { string name; cout << "请输入您要修改的姓名:" << endl; cin >> name; abs->personArray[ret].m_Name = name; cout << "请输入您要修改的性别:" << endl; cout << "1----男" << endl; cout << "2----女 " << endl; int sex = 0; while (true) { //注意观察这个循环 cin >> sex; if (sex == 1 || sex == 2) { abs->personArray[ret].m_Sex = sex; break; } cout << "您输入的性别有误,请您重新输入。" << endl; } cout << "请输入您要修改的年龄: " << endl; int age = 0; cin >> age; abs->personArray[ret].m_Age = age; cout << "请输入您要修改的联系电话: " << endl; string phone; cin >> phone; abs->personArray[ret].m_Phone = phone; cout << "请输入您要修改的家庭地址: " << endl; string address; cin >> address; abs->personArray[ret].m_Addr = address; cout << "联系人已经修改成功了。" << endl; } else { cout << "没有这个联系人呢" << endl; } system("pause"); system("cls"); } //第六个模块开始了,清空联系人。 void cleanPerson(Addressbooks* abs) { cout << "您是否真的要清空通讯录里的全部联系人吗?" << endl; cout << "确定删除的话请输入1,取消删除请输入0。" << endl; while (true) { int ret; cin >> ret; if (ret == 1 || ret == 0) { if (ret == 1) { abs->m_size = 0; cout << "联系人现在已经全部清空了。" << endl; break; } else { cout << "联系人未删除,谢谢操作。" << endl; break; } } else { cout << "您的输入有误,请重新输入。" << endl; } } system("pause"); system("cls"); } int main() { Addressbooks abs;//结构体变量 abs.m_size = 0; while (true) { showMenu(); int select; cin >> select; switch (select) { case 1: addPerson(&abs); break; case 2: showPerson(&abs); break; case 3: deletePerson(&abs); break; case 4: findPerson(&abs); break; case 5: modifyPerson(&abs); break; case 6: cleanPerson(&abs); break; case 0: cout << "欢迎下次使用钟一淼的通讯录系统呀!" << endl; system("pause"); return 0;//可以直接跳出这个循环switch break; default: break; } } system("pause"); return 0; }
用map写的版本后期会更新,哎,世界上走过最难的路,c++的套路a
map例题:P5266 【深基17.例6】学籍管理
题目描述
您要设计一个学籍管理系统,最开始学籍数据是空的,然后该系统能够支持下面的操作(不超过 10^5105 条):
- 插入与修改,格式
1 NAME SCORE
:在系统中插入姓名为 NAME(由字母和数字组成不超过 20 个字符的字符串,区分大小写) ,分数为 \texttt{SCORE}SCORE(0<\texttt{SCORE}<2^{31}0<SCORE<231) 的学生。如果已经有同名的学生则更新这名学生的成绩为 SCORE。如果成功插入或者修改则输出OK
。- 查询,格式
2 NAME
:在系统中查询姓名为 NAME 的学生的成绩。如果没能找到这名学生则输出Not found
,否则输出该生成绩。- 删除,格式
3 NAME
:在系统中删除姓名为 NAME 的学生信息。如果没能找到这名学生则输出Not found
,否则输出Deleted successfully
。- 汇总,格式
4
:输出系统中学生数量。输入格式
无
输出格式
无
输入输出样例
输入 #1复制
5 1 lxl 10 2 lxl 3 lxl 2 lxl 4输出 #1复制
OK 10 Deleted successfully Not found这道题的解题重点就是要掌握map的映射,map<string ,int>s,即可用s[name]=score,即name为s的下标。
#include <iostream> #include <map> using namespace std; map<string, int>s; int n,m; string name; int score; int main() { cin >> n; while (n--) { cin >> m; if (m == 1) { cin >> name >> score; s[name] = score; cout << "OK" << endl; } else if (m == 2) { cin >> name; if (s.find(name) != s.end()) { cout << s[name] << endl; } else { cout << "Not found" << endl; } } else if (m == 3) { cin >> name; if (s.find(name) != s.end()) { s.erase(s.find(name)); cout << "Deleted successfully" << endl; } else { cout << "Not found" << endl; } } else { cout << s.size() << endl; } } }
本蒟蒻对集合的总结已经写完了,重点还是把这几个代码和函数的原理弄清楚,才学疏浅,部分内容借鉴《深入浅出》书上的内容。
集合结束,今晚或明天转战图的基本应用,加油,钟一淼!