栈
栈和队列相似但是不同,不同的是队列先进先出从末尾进排头出,栈像一个瓶子容器,从瓶口进,瓶口出,先进的在瓶底所以后出,所以栈的特点 先进后出,后进先出。然后下面讲用法和例题。
#include<stack>
stack<char(可以是int等)> q(名字);
q.push(a); //将a入栈
q.pop(); //将栈顶出栈
q.top(); //栈顶的值
q.empty(); //判断是否为空
例题:假设一个表达式有英文字母(小写)、运算符(+,—,*,/)和左右小(圆)括号构成,以“@”作为表达式的结束符。请编写一个程序检查表达式中的左右圆括号是否匹配,若匹配,则返回“YES”;否则返回“NO”。表达式长度小于255,左圆括号少于20个。
输入输出格式
输入格式:
一行:表达式
输出格式:
一行:“YES” 或“NO”
输入输出样例
输入样例#1:
2*(x+y)/(1-x)@
输出样例#1:
YES
输入样例#2:
(25+x)(a(a+b+b)@
输出样例#2:
NO
题解:将所有左括号挨着入栈,然后每次出现右括号就去判断栈里面是否有左括号,如果有就继续,没有说明不合理就NO
代码:
#include<stdio.h>
#include<stdlib.h>
#include<math.h>
#include<string.h>
#include<queue>
#include<algorithm>
#include<iostream>
#define exp 1e-9
#define PI acos(-1.0)
#define INF 0x3f3f3f3f
using namespace std;
typedef unsigned long long LL;
stack<char> zhan;
int main()
{
char input;
while(scanf("%c",&input)&&input!='@')
{
if(input=='(')
zhan.push(input);//入栈
if(input==')') {
if(zhan.empty()) //判断栈是否为空即可
{
printf("NO\n");//也许大家会问,为啥不来个if(zhan.top()=='(' )呢,因为 这题只有小括号 而且会超时
return 0;//至于为啥要判断栈是否为空,大家想想,因为假如有个),结果前面没有对应(了,那就不行,此时就是栈空
}
zhan.pop();//出栈
}
}
if(zhan.empty())
printf("YES\n");//判断有没有多余的(
else
printf("NO\n");//也就是判断栈是否为空
return 0;
}
队列
队列就是一行人排在一起,先进去的人排在第一个,后进去的人排在后面,所以队列的特点先进先出,这个是很重要的。在处理一些特殊问题的时候,队列是非常有用的。下面主要讲用法和例题。
#include<queue> //要包含头文件
int a,b,c;
queue<int> q1; //定义q1 队列 <>中的为数据类型
q1.push(3); //把3压入队列,接到队列的末端
q1.pop(); //删除队列的第一个元素,注意,并不会返回被弹出元素的值,只是删除
a = q1.front(); //返回队例首元素 ,不删除, a为队列的第一个元素
b = q1.back(); //同上一样,返回队例尾元素
c = q1.size(); // c等于q1现在包含的元素的个数【个数】
q.empty() //q队列为空时返回真。
例题:n个人(n<=100)围成一圈,从第一个人开始报数,数到m的人出列,再由下一个人重新从1开始报数,数到m的人再出圈,……依次类推,直到所有的人都出圈,请输出依次出圈人的编号.
输入输出格式
输入格式:
n m
输出格式:
出圈的编号
输入输出样例
输入样例#1:
10 3
输出样例#1:
3 6 9 2 7 1 8 5 10 4
说明
m,n≤100
题解:这是一道比较典型的队列题,一个一个进入队列,报道m的就出列然后输出,不是m的就继续排到末尾。
代码
#include<stdio.h>
#include<stdlib.h>
#include<math.h>
#include<string.h>
#include<queue>
#include<algorithm>
#include<iostream>
#define exp 1e-9
#define PI acos(-1.0)
#define INF 0x3f3f3f3f
using namespace std;
typedef unsigned long long LL;
int main()
{
int n,m,nownum=1;
queue<int>q;
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
q.push(i); // 入队
while(!q.empty())
{
if(nownum==m)
{
printf("%d ",q.front()); //输出编号
q.pop(); //出队
nownum=1;
}
else
{
nownum++;
q.push(q.front()); //排至队尾
q.pop();//出队
}
}
return 0;
}
优先队列
自动排序的队列
#include<queue>
using namespace std;
这两个头文件。
其次,一个优先队列声明的基本格式是:
priority_queue<结构类型> 队列名;
比如
priority_queue <int> q;
//这样是从大到小拿出
priority_queue<int , vector<int> , greater<int> >q;
//这样是从小到大拿出
q.size();//返回q里元素个数
q.empty();//返回q是否为空,空则返回1,否则返回0
q.push(k);//在q的末尾插入k
q.pop();//删掉q的第一个元素
q.top();//返回q的第一个元素
q.back();//返回q的末尾元素
例题:给定一个木条,锯成input中要求的段数和每一段的长度,每锯一次花费锯成被锯成两半的木条长度之和,求最小花费
题解:每次把最小的两个数合并就行。用优先队列处理合适。
#include<stdio.h>
#include<stdlib.h>
#include<math.h>
#include<string.h>
#include<algorithm>
#include<iostream>
#include<queue>
#include<stack>
#include<vector>
#include<map>
#define exp 1e-9
#define PI acos(-1.0)
#define INF 0x3f3f3f3f
#define mod 1000000007
using namespace std;
typedef long long LL;
int a[20005];
int ans=0,n;
void solve()
{
priority_queue<int , vector<int> , greater<int> >q; //定义从小到大拿出的优先队列
for(int i=1;i<=n;i++)
q.push(a[i]); //全部入队
while(q.size()>1)
{
int l1=q.top();
q.pop();
int l2=q.top();
q.pop();
ans+=(l1+l2);
q.push(l1+l2); //把最小两个的和继续入队
}
}
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
scanf("%d",&a[i]);
solve();
printf("%d\n",ans);
return 0;
}
例题:你须要开着一辆卡车行驶L单位距离,最開始卡车有P单位汽油,卡车每开一单位距离须要消耗1单位汽油。如果在中途卡车汽油耗尽,卡车就无法继续前行。到不了终点。在途中一共同拥有n个加油站。告诉你每一个加油站距离终点的距离和每一个加油站能够加的油量,如果卡车的油箱是无穷大的,问卡车至少要加多少次油才干到达终点?卡车到不了终点输出-1;
输入n,l,p。n为加油站数目,l为距离,p为油量
输入n个数据a[i] 代表每个加油站的位置
输入n个数据b[i] 代表每个加油站对应可以加的油量
题解:把思路转化为路过一个加油站,就先放入优先队列中,因为优先队列可以把最大的放在最上面。然后只要油量不够,就拿出优先队列最上面个,如果拿不出,则输出-1无法到达。
#include<stdio.h>
#include<stdlib.h>
#include<math.h>
#include<string.h>
#include<algorithm>
#include<iostream>
#include<queue>
#include<stack>
#include<map>
#define exp 1e-9
#define PI acos(-1.0)
#define INF 0x3f3f3f3f
#define mod 1000000007
using namespace std;
typedef long long LL;
int n,l,p;
int a[105],b[105];
void solve()
{
a[n]=l;
b[n]=0;
n++;
//终点看成加油站
priority_queue<int>q;
int ans=0,pos=0,tank=p;
//ans为加油次数,pos为当前位置,tank为当前油量
for(int i=0;i<n;i++)
{
int d=a[i]-pos;
//油量不够
while(tank-d<0)
{
if(q.empty())
{
printf("-1\n");
return;
}
tank+=q.top();
q.pop();
ans++;
}
//油量足够
tank-=d;
pos=a[i];
q.push(b[i]);
}
printf("%d\n",ans);
}
int main()
{
scanf("%d%d%d",&n,&l,&p);
for(int i=0;i<n;i++)
scanf("%d",&a[i]);
for(int i=0;i<n;i++)
scanf("%d",&b[i]);
solve();
return 0;
}
map
map的基本操作函数:
C++ maps是一种关联式容器,包含“关键字/值”对
begin() 返回指向map头部的迭代器
clear() 删除所有元素
count() 返回指定元素出现的次数
empty() 如果map为空则返回true
end() 返回指向map末尾的迭代器
equal_range() 返回特殊条目的迭代器对
erase() 删除一个元素
find() 查找一个元素
get_allocator() 返回map的配置器
insert() 插入元素
key_comp() 返回比较元素key的函数
lower_bound() 返回键值>=给定元素的第一个位置
max_size() 返回可以容纳的最大元素个数
rbegin() 返回一个指向map尾部的逆向迭代器
rend() 返回一个指向map头部的逆向迭代器
size() 返回map中元素的个数
swap() 交换两个map
upper_bound() 返回键值>给定元素的第一个位置
value_comp() 返回比较元素value的函数
例题:Registration system
在不久的将来,一种新的电子邮件服务 “Berlandesk” 将在 Berland 开放。网站管理员想要尽快发布他们的项目,因此请您提供帮助。请实现网站注册系统的原型。该系统应当按以下的原理工作。
每当一个新的用户想要注册时,他向系统发送一个包含了自己 name 的请求。如果系统数据库中不存在这样的 name ,则它被插入到数据库中,并且用户获得反馈 OK ,确认注册成功。如果系统数据库中已存在该 name ,则系统创建一个新的用户名,将它发送给用户作为提示,且将提示信息也插入数据库中。新的用户名按如下规则构成。以 1 开头的数字被逐一追加到 name 的尾部 (name1, name2, …),其中最小的 i 被找到,使得数据库中尚不存在 namei 。
输入
第一行包含了数字 n (1?≤?n?≤?105)。接下来的 n 行包含了对系统的请求。每个请求是非空的一行,由不超过 32 个字符组成,各字符均为小写的拉丁字母。
输出
打印 n 行,作为对请求作出的系统反馈:如果注册成功,则打印 OK ;如果请求的用户名已被占用,则打印新的用户名。
#include<stdio.h>
#include<stdlib.h>
#include<math.h>
#include<string.h>
#include<algorithm>
#include<iostream>
#include<queue>
#include<stack>
#include<map>
#define exp 1e-9
#define PI acos(-1.0)
#define INF 0x3f3f3f3f
using namespace std;
typedef long long LL;
char str[1000050];
int main()
{
int n;
map<string,int> s;
string str;
while(~scanf("%d",&n))
{
cin>>str;
s[str]++;
if(s[str]>1)
cout<<str<<s[str]-1<<endl;
else
printf("OK\n");
}
return 0;
}
set
set,顾名思义,就是数学上的集合——每个元素最多只出现一次,并且set中的元素已经从小到大排好序。
头文件:#include
常用的函数:
begin() 返回set容器的第一个元素的地址
end() 返回set容器的最后一个元素地址
clear() 删除set容器中的所有的元素
empty() 判断set容器是否为空
max_size() 返回set容器可能包含的元素最大个数
size() 返回当前set容器中的元素个数
erase(it) 删除迭代器指针it处元素
insert(a) 插入数据a
例题:
洛谷P1059 明明的随机数
题目描述
明明想在学校中请一些同学一起做一项问卷调查,为了实验的客观性,他先用计算机生成了N个1到1000之间的随机整数(N≤100),对于其中重复的数字,只保留一个,把其余相同的数去掉,不同的数对应着不同的学生的学号。然后再把这些数从小到大排序,按照排好的顺序去找同学做调查。请你协助明明完成“去重”与“排序”的工作。
输入输出格式
输入格式:
输入有两行,第11行为11个正整数,表示所生成的随机数的个数N
第2行有N个用空格隔开的正整数,为所产生的随机数。
输出格式:
输出也是两行,第1行为1个正整数M,表示不相同的随机数的个数。
第2行为M个用空格隔开的正整数,为从小到大排好序的不相同的随机数。
输入输出样例
输入样例#1: 复制
10
20 40 32 67 40 20 89 300 400 15
输出样例#1: 复制
8
15 20 32 40 67 89 300 400
题解
利用了SET容器的特性每个元素只有一个。
代码
#include<bits/stdc++.h>
using namespace std;
set<int>s;
int a[105];
int main()
{
int n;
cin>>n;
for(int i=0;i<n;i++)
{
cin>>a[i];
s.insert(a[i]);
}
cout<<s.size()<<endl;
while(!s.empty())
{
cout<<*s.begin()<<" "; //注意此处的用法
s.erase(s.begin());
}
return 0;
}
Vector
vector(向量): C++中的一种数据结构,确切的说是一个类.它相当于一个动态的数组,当程序员无法知道自己需要的数组的规模多大时,用其来解决问题可以达到最大节约空间的目的.
1.文件包含:
首先在程序开头处加上#include<vector>以包含所需要的类文件vector
还有一定要加上using namespace std;
2.变量声明:
2.1 例:声明一个int向量以替代一维的数组:vector <int> a;(等于声明了一个int数组a[],大小没有指定,可以动态的向里面添加删除)。
2.2 例:用vector代替二维数组.其实只要声明一个一维数组向量即可,而一个数组的名字其实代表的是它的首地址,所以只要声明一个地址的向量即可,即:vector <int *> a.同理想用向量代替三维数组也是一样,vector <int**>a;再往上面依此类推.
详细的函数实现功能:其中vector c.
c.clear() 移除容器中所有数据。
c.empty() 判断容器是否为空。
c.erase(pos) 删除pos位置的数据
c.erase(beg,end) 删除[beg,end)区间的数据
c.front() 传回第一个数据。
c.insert(pos,elem) 在pos位置插入一个elem拷贝
c.pop_back() 删除最后一个数据。
c.push_back(elem) 在尾部加入一个数据。
c.resize(num) 重新设置该容器的大小
c.size() 回容器中实际数据的个数。
c.begin() 返回指向容器第一个元素的迭代器
c.end() 返回指向容器最后一个元素的迭代器