各种使用函数可以看别的博客学习,这里重点讲应用。
1.vector
- []遍历,emplace_back,size,clear
- sort(v.begin(),v.end());
应用:
特别是要开二维数组,要节省空间的时候而且长度不定的时候特别好用,图中的邻接表就是很好的例子。
邻接表中一维数组存储顶点,一维vector存储该顶点的所有邻接点,避免开二维数组(邻接矩阵)带来的空间浪费,稀疏图尤其好用。
有时题目要输出路径,可以建立个vector
uva101,木块问题,类似的。
写法:
vector<int>vex[maxn];
2.set
- insert,erase(数据),count,clear
应用:输出不重复的、查找(logn因为内部是红黑树,即平衡二叉树)
uva11572
输入一个长度为n的序列A,找到一个尽量长的连续子序列,使得该序列中没有相同的元素。
#include<bits/stdc++.h>
using namespace std;
//插入count,,insert erase
int a[100000005];
int main()
{
int T;
cin>>T;
int n;
while(T--)
{
cin>>n;
for(int i=0;i<n;i++)
{
cin>>a[i];
}
set<int>temp;
int l,r,ans;
l=r=ans=0;
while(r<n)//本来写l<n,多此一举
{
while(r<n&&!temp.count(a[r]))
{
temp.insert(a[r]);
r++;
}
ans=max(ans,r-l);
temp.erase(a[l]);
l++;
}
cout<<ans<<endl;
}
}
3.map、multimap
- [],insert,count和find函数,clear
应用:
①统计出现次数
以前统计字符出现次数,用的count[a[i]-'0'],其实就是用到map“关联数组”的思想。
#include<bits/stdc++.h>
#include<map>
using namespace std;
map<char,int>a;//常用string,int
int main()
{
char m[100];
cin>>m;
int max1=0;
for(int i=0;i<strlen(m);i++)
{
a[m[i]]++;//若不定义map用char数组,则要m[i]-'0'
max1=max(max1,a[m[i]]);//字符最多出现次数
}
cout<<max1<<endl;
}
还是上面的uva 11572
#include<bits/stdc++.h>
using namespace std;
//处理重复?→i,j 集合/出现次数 ==0→count[]++→ans→ --到!=temp
map<int,int>a;
int count1[10000005];
int main()
{
int T;
cin>>T;
int n;
while(T--)
{
cin>>n;
for(int i=0;i<n;i++)
{
cin>>a[i];
}
int temp=-1;
int i,j,ans;
i=j=ans=0;
while(i<n)//尺取法
{
while(j<n&&count1[a[j]]==0)
{
count1[a[j]]++;
j++;
temp=a[j];
}
ans=max(ans,j-i);
while(a[i]!=temp)
{
count1[a[i]]--;
i++;
}
if(a[i]==temp)//刚好到重复数字的位置
{
count1[a[i]]--;
i++;
}
}
cout<<ans<<endl;
a.clear();
}
}
②分配“ID”判匹配,离散化
也是邻接表的例子,有时题目输入的是顶点名字而不是序号,《数据结构》课本给的方法是,另外用个string vex[]静态数组,但是每次都是顺序查找,要用O(n)时间,而map查找运用红黑树的原理,查找、插入、删除等操作仅用O(logn)时间。
pta QQ密码与账号的注册和登录
输入格式:
输入首先给出一个正整数N(≤),随后给出N行指令。每行指令的格式为:“命令符(空格)QQ号码(空格)密码”。其中命令符为“N”(代表New)时表示要新申请一个QQ号,后面是新帐户的号码和密码;命令符为“L”(代表Login)时表示是老帐户登陆,后面是登陆信息。QQ号码为一个不超过10位、但大于1000(据说QQ老总的号码是1001)的整数。密码为不小于6位、不超过16位、且不包含空格的字符串。
输出格式:
针对每条指令,给出相应的信息:
1)若新申请帐户成功,则输出“New: OK”;
2)若新申请的号码已经存在,则输出“ERROR: Exist”;
3)若老帐户登陆成功,则输出“Login: OK”;
4)若老帐户QQ号码不存在,则输出“ERROR: Not Exist”;
5)若老帐户密码错误,则输出“ERROR: Wrong PW”。
输入样例:
5
L 1234567890 [email protected]
N 1234567890 [email protected]
N 1234567890 [email protected]
L 1234567890 myQQ@qq
L 1234567890 [email protected]
输出样例:
ERROR: Not Exist
New: OK
ERROR: Exist
ERROR: Wrong PW
Login: OK
#include <bits/stdc++.h>
using namespace std;
map<string,int>p;//号码与0/1(是否存在)的联系
map<string,string>q;//号码与密码的联系
int main()
{
int n;
cin>>n;
char a;
string HM;
string MM;
while(n--)
{
cin>>a>>HM>>MM;
if(a=='N')
{
if(p[HM]==0)
{
cout<<"New: OK"<<endl;
p[HM]=1;
q[HM]=MM;
}
else
{
cout<<"ERROR: Exist"<<endl;
}
}
else
{
if(p[HM]==1)
{
if(q[HM]==MM)
{
cout<<"Login: OK"<<endl;
}
else
{
cout<<"ERROR: Wrong PW"<<endl;
}
}
else
{
cout<<"ERROR: Not Exist"<<endl;
}
}
}
}
4.queue,deque,stack
- size(),front(top),pop(),push(),empty()
- 定义 queue<double> q; deque<double> q;
访问队列中的元素个数 q.size() q.size()
判断队列空 q.empty() q.empty()
尾部压入新元素 q.push(x) q.push_back(x)
头部压入新元素 EOF q.push_front(x)
弹出队列的第一个元素 q.pop() q.pop_front()
弹出队列的最后一个元素 EOF q.pop_back()
访问队首元素 q.front() (访问迭代器)q.begin()
访问队尾元素 q.back() (同上)q.end()
删除所有元素 EOF q.clear()
删除任意位置x EOF q .erase(q.begin() + x)
(某大佬总结)
应用:前者用的较多,如宽搜以及宽搜引出的各种算法。
deque有些滑动窗口类型题会用到
后者模拟题会用到,如括号匹配,但一般尽量用递归实现。
拓扑排序队列实现:
#include <iostream>
#include <cstring>
#include <cstdio>
#include <queue>
using namespace std;
const int maxn=1e6+10;
int n,m,t;
int G[1000][1000],id[1000],topo[1000];
void toposort()
{
queue<int> q;
int t=0;
for(int i=1;i<=n;i++)
if(id[i]==0)
q.push(i);//入度为零的放进队列
while(!q.empty())
{
int u=q.front();
q.pop();
topo[t++]=u;//入度为零的就是排好的,拿出来
for(int i=1;i<=n;i++)//这个循环用来删除拿出来的点和相连接的点的边
if(G[u][i]!=0)
if(--id[i]==0)
q.push(i);
}
printf("%d",topo[0]);
for(int i=1;i<t;i++)
printf(" %d",topo[i]);
printf("\n");
}
int main()
{
while(scanf("%d%d",&n,&m) && (m!=0 || n!=0))
{
memset(G,0,sizeof(G));
memset(id,0,sizeof(id));
int a,b;
for(int i=1;i<=m;i++)
{
scanf("%d%d",&a,&b);
G[a][b]=1;
id[b]++;//记录每个点的入度
}
toposort();
}
return 0;
}
5.priority_queue
注意取队首写法是top(),还有重载运算符写法。
应用:
①贪心:
之前说贪心前一般有排序操作,如果是静态取一般是直接sort,但有时变化是动态的,取出或修改一个元素可能会对整体顺序造成影响,需要时刻排序,这时用priority_queue。
poj 2431 3614 2010 pku 3253
②宽搜:
宽搜一般用queue,但在有些问题中搜索权值不同,导致搜索顺序有影响,用优先队列。
2018湘潭程序设计竞赛一道题,我的题解:
6.list
- splice
应用:线性结构(包括栈和队列)超时的时候,特别是合并操作,用链式结构往往有出其不意的效果。
zoj4016 Mergeable Stack
题意:对n个栈,有q次操作。每个操作可能为三种情况中的一种:1.将v插入到s栈的顶端;2.输出s栈的栈顶(若栈为空则输出empty);3.将栈t插入到栈s的栈顶。(合并)
#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<cstdlib>
#include<set>
#include<vector>
#include<stack>
#include<map>
#include<queue>
#include<list>
#include<algorithm>
using namespace std;
const int maxn=300005;
list<int> l[maxn];
int main()
{
int n,q,t;
scanf("%d",&t);
while(t--)
{
scanf("%d%d",&n,&q);
for(int i=1; i<n+1; i++)l[i].clear();
while(q--)
{
int op;
scanf("%d",&op);
if(op==1)
{
int s,v;
scanf("%d%d",&s,&v);
l[s].push_back(v);
}
if(op==2)
{
int s;
scanf("%d",&s);
if(l[s].empty())puts("EMPTY");
else
{
printf("%d\n",l[s].back());
l[s].pop_back();
}
}
if(op==3)
{
int s,t;
scanf("%d%d",&s,&t);
l[s].splice(l[s].end(),l[t]);
}
}
}
return 0;
}