目录
个人介绍
前言
map——映射
本质
定义方法
时间复杂度
例题1:
例题2:
代码:
总结:
vector容器
基本操作
例题1
代码
分析
例题2
代码
pair的用法
基本介绍
用法介绍
例题
分析
代码
总结
优先队列
简介
定义方式
操作方法
应用:迪杰斯特拉最短路
set容器
基本功能
操作方法
操作方法1:查找
例题:
代码:
操作方法二:找出集合不同的元素的个数
题目1
分析
代码
题目2
分析
代码
操作方法三:
例题
分析
代码
操作方法四
例题
分析
代码
各位CSDN的友友们,大家好,我是小熙,一个算法小透明,希望在CSDN的大舞台可以和大家一起取得进步(^_^)
个人现状:上个学期在学校的ACM小组学了一个学期的算法,但是世界很大,我的梦想很远,我希望在我年轻的时候可以走出国门,到世界的其他地方看一看,再加上我的算法功底可能真的不太足以支持我在ACM比赛上做出成绩。所以我选择逐渐淡出ACM小组,并把之前的知识整理成博客供大家分享。
学习算法的小方法:知道算法的基本原理——>知道代码实现——>总结出代码模板并熟练于心——>基本模板题——>变式思维题
每当讲到C++STL容器啥的,你是不是总会看到下面的玩意
怎么样,是不是头大了
但下面,我们就从题目的角度入手,来看看各大容器的哪些用法是刷题有用的,哪些是刷题没用的,至于那些刷题没用的,不会也没关系。引用ACM区域赛金牌大神刁鹏杰学长的话——我们是做题家,不是理论家。
表示的是一一映射的关系。
map定义方法:
map
a; (1)第一个关键字可以理解为关键字key,每个关键字在map中只出现一次,第二个可以理解为关键字的值value。
(2)key和value可以是任何类型,可以是int,double,string。比如map
a;map a;
单次维护和查询的时间复杂度为O(logn)非常优秀
map可以用来计数,map
book通常是桶排序的数组 。book[2]就表示的是数组中2出现的次数。例:给定一个数组a,它的大小长度为n,数组的里面的数据为a1,a2,a3........an。输入一个数据k,判断k在数组中出现的次数。
样例:
5
2 3 5 2 4
2
输出:
2
#include
using namespace std; map book; int n; int main() { cin>>n; for(int i=1;i<=n;i++) { int temp; cin>>temp; book[temp]++; } int k; cin>>k; cout<
总结:桶计数是map一个功能之一
P5266 【深基17.例6】学籍管理 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
#include
using namespace std;
map book;
int main()
{
int n;
cin>>n;
for(int i=1;i<=n;i++)
{
int x;//x表示的命令
cin>>x;
if(x==1)//表示的命令1
{
string t;
int y;
cin>>t>>y;
book[t]=y;
cout<<"OK"<>t;
if(!book.count(t)) cout<<"Not found"<>t;
if(!book.count(t)) cout<<"Not found"<
(1):这个题将map的一一映射的特性体现的淋漓尽致。
(2):以map
book为例,book.size()表示的是映射容器book中的元素的个数 (3):以map
book为例,book.count(key)表示查找关键字key的值是否存在 (4):以map
book为例,book.earse(key)表示清空关键字key的值
定义:例如vector
a,int还可以是double,long long,pair等数据类型 操作:
(1):以vector
a为例子,a.push_back(temp)表示向动态数组里面新加入值为temp的元素 (2):以vector
a为例子,a.size( )计算动态数组a中的元素的个数 (3):以vector
a为例子,!a.empty()查看动态数组a是否为空
P2671 [NOIP2015 普及组] 求和 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
#include
using namespace std;
typedef long long ll;
const ll mod=10007;
const ll N=1e5+5;
struct node{
ll num;
ll color;
}k[N];
vectore1[N];
vectore2[N];
int main()
{
ll n,m;cin>>n>>m;
ll sum=0;
for(int i=1;i<=n;i++)
{
cin>>k[i].num;
}
for(int i=1;i<=n;i++)
{
cin>>k[i].color;
if(i&1) e1[k[i].color].push_back(i);
else e2[k[i].color].push_back(i);
}
for(int i=1;i<=m;i++)
{
int len=e1[i].size();
ll a,b;
for(int l=0;l
这道题比较考察数学分析,但也不是特别复杂。不难看出决定三元组变量结果的只有x,y。但是x与y的和是2z,是一个偶数,所以x与y的奇偶性一定要是相同的。然后利用vector进行分类计算就可以了。
#include
using namespace std;
const int N=1e5+10;
vectore[N];//e[i]表示第i个帖子的ID编号集
int main()
{
int n;
cin>>n;
for(int i=1;i<=n;i++)
{
char temp[6];
int x,y;
cin>>temp>>x>>y;
if(temp[0]=='A')
{
e[x].push_back(y);
}
if(temp[0]=='Q')
{
if(y>e[x].size()+1)
{
cout<<"-1"<
总结:有的时候像例一这种数学分类的题和例二这种一对多(一个帖子有多个ID号码)的题目用下vector可能更方便。
个人理解:可以把pair
理解为数据类型,地位等同double,int,long long。pair a[N]就是pair类型的数组。 引用元素:pair
a为例,a.first表示引用的是第一个元素,a.second表示引用的是是第二个元素。 关于排序:用STL sort排序pair类型的数组按照的是first元素排列的。
类似结构体,不过只有两个元素,把这两个元素捆绑在一起。通常在把二维坐标(x,y)或者直线(斜率k和参数b)捆绑在一起当pair。
前面在用法介绍那里讲了,看到直线就要想到捆绑"k"和“b”用pair
#include
using namespace std; const int N=1e3+10; int cnt; struct node{ int x; int y; }e[N]; typedef pair pdd; set a;//存储直线 int main() { for(int i=0;i<=19;i++) { for(int j=0;j<=20;j++) { e[++cnt].x=i; e[cnt].y=j; } } cout<
(1)关于两点的直线的结论
(2)直线相关的问题好要特判平行的情况。
与队列的不同是它可以自定义数据的优先级,让优先级高的排在前面,优先出队。
priority_queue<数据类型,容器类型,比较方式>
例如:priority_queue
,greater > q是大根堆又称升序队列 priority_queue
,less > q是小根堆又称降序队列
*top:访问的是队头的元素
*size:队列中元素的个数
*push:把元素加到优先队列里面去
*empty:优先队列是否为空
我水平真的非常有限,只知道优先队列它有这种用法了,抱歉,各位读者。迪杰斯特拉会在图论专题中介绍,先添加一个模板放在这里。
#include
using namespace std; typedef pair pii; const int N=2e5+5; typedef long long ll; int n,m,s;//s表示的是坐标源点 ll dis[N]; bool vis[N]; vector e[N]; priority_queue ,greater > q; void dijsktra() { memset(dis,0x3f,sizeof(dis)); dis[s]=0; memset(vis,0,sizeof(vis)); //vis和dis初始化,优先队列 q.push(make_pair(0,s)); //加入源点loo while(!q.empty()) { int u=q.top().second; q.pop(); if(vis[u]) continue; vis[u]=1;//表示已经入过一次队了 for(int i=0;i dis[u]+w) { dis[v]=dis[u]+w; q.push(make_pair(dis[v],v)); } } } //跑迪杰斯特拉 } int main() { cin>>n>>m>>s; for(int i=1;i<=m;i++) { int x,y,z; cin>>x>>y>>z; e[x].emplace_back(y,z); } dijsktra(); for(int i=1;i<=n;i++) cout<
拥有自动去重和排序的作用。
前言:set的操作方法太多了,一把列举出来的话容易让人云里雾里,就以题目的方式来讲述set的操作方法吧。
(1)以set
a为例子,a.count(k)表示set集合里面是否存在k这个数据。 (2)以set
a为例子,a.find(k)==a.end()表示set集合里面不存在k这个数据。反之表示存在k这个数据。 总结:以上两种操作方法的作用是查找。
给出一个长度大小为n的数组a,数组中的元素个数为a1,a2,a3.......an-1,an,请输入一个数字k,判断数组中是否有k这个数,有的话输入"OK"无的话输入“NO”
样例:
5
2 3 2 4 6
4
输出
OK
#include
using namespace std;
const int N=1e3+10;
seta;
int main()
{
int n;
cin>>n;
for(int i=1;i<=n;i++)
{
int t;
cin>>t;
a.insert(t);
}
int k;
cin>>k;
if(a.count(k)) cout<<"OK";
else
cout<<"NO"<
#include
using namespace std;
const int N=1e3+10;
seta;
int main()
{
int n;
cin>>n;
for(int i=1;i<=n;i++)
{
int t;
cin>>t;
a.insert(t);
}
int k;
cin>>k;
if(a.find(k)!=a.end()) cout<<"OK";
else
cout<<"NO"<
以set
a为例子,a.size()表示的是集合a中不同元素的个数
P3370 【模板】字符串哈希 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
看到不同二字,立马想到set,然后没了。
#include
using namespace std;
int main()
{
int n;cin>>n;
string p;
seta;
for(int i=1;i<=n;i++)
{
cin>>p;
a.insert(p);
}
cout<
转化为斜率k来分析,求斜率k不同的数量
#include
using namespace std;
seta;
int cnt;
int main()
{
int n;cin>>n;
double x0,y0;
cin>>x0>>y0;
for(int i=1;i<=n;i++)
{
double x1,y1;
cin>>x1>>y1;
if(x1==x0)
{
cnt=1;
continue;
}
double temp=(y1-y0)/(x1-x0);
a.insert(temp);
}
cout<
(1):auto定义stl的迭代器,在遍历stl的时候需要用迭代器来遍历。这里的迭代器可以把他理解为指针。
(2):a.lower_bound(x)返回的是迭代器,在写的时候经常写成auto it=lower_bound(x)
(3):以set
a为例,a.erase(x)表示的是删除操作,删除set集合中的x值
P5250 【深基17.例5】木材仓库 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
看到“保障没有两个木材长度相同”,直接set
#include
using namespace std;
seta;
int n;
int main()
{
int n;cin>>n;
while(n--)
{
int x,y;
cin>>x>>y;//x表示的是命令,1的话就是进货,2的话就是出货
if(x==1)
{
if(!a.count(y))
{
a.insert(y);
}
else
{
cout<<"Already Exist"<
迭代器iterator,遍历容器的作用。
通常写法:for(set
::iterator it=a.begin():it!=a.end();it++)
本题虽然与集合相关,但是把set中的元素提取到数组里面的过程用了上面讲的迭代器的遍历
#include
using namespace std; typedef pair pdd; set a;//先用set容器装直线,对直线进行去重操作 const int N=1e3+10; pdd b[N];//以数组的方式存储去重之后的直线 int n; int main() { cin>>n; for(int i=1;i<=n;i++) { double x,y; cin>>x>>y; a.insert({x,y}); } int cnt=0; for(set ::iterator it=a.begin();it!=a.end();it++,++cnt)//这是以迭代器的方式遍历set容器里面的每一个元素,背下来就可以了,反正搞明白也意义不大 { double x=it->first;double y=it->second; pdd temp; temp.first=x; temp.second=y; b[cnt]=temp; }//这一个部分的作用遍历set里面的每一个元素然后把他提取到数组b里面去 int ans=2; //这一个步骤就是枚举直线了,第一层循环枚举2~i的每一个直线,第二层循环是第i条直线之前二代每一条直线。 for(int i=2;i<=cnt;i++) { set t;//set t用来装第i条加入的直线与之前加入的直线的交点 for(int j=1;j