C++标准模板库知识点:
/*一、主要内容:
sort //必会!
next_permutation() //记住怎么调用
list //
stack //调用类库
queue //调用类库
vector //是一个容器,主要用于树和图的存储
set
迭代器 //以上两个主要用于处理集合,判重,排序
map //是一个映射,处理某些映射关系
dequedu //双端队列
priority_queue //优先队列(堆排)
*/
/*二、C++标准模板库(Standard Template Library)
含有很多常用的算法与数据结构,使用时只需要一行代码调用出来实现各种操作即可,不需要自己写很长的代码。
能极大地简化编程减少BUG。
例如:①最大值最小值(别看简单,c语言自身却没有这个)
template<class_Tp>_Tp min(_Tp a,_Tp b)
template<class_Tp>_Tp max(_Tp a,_Tp b)
②快速排序:sort(a,a+10);
③数据交换:swap(a,b);
④求下一个排列:next_permutation
⑤顺序式容器:
vector 不限制长度的数组
list 链表
deque 双向队列
queue 队列
priority_queue 优先队列
stack 栈
⑥关联式容器:
set 集合(一种较高效率的平衡二叉树)(可判重)
multiset (不判重)
map 映射(通过键值来建立的平衡二叉树)
multimap
*/
/*2.1 sort:(见eg1、eg2)
STL排序函数sort():是最常用的函数之一
定义有两种:
void sort(RandomAccessIterator first,RandomAccessIterator last);
void sort(RandomAccessIterator first,RandomAccessIterator last,Compare comp);
返回值:无。
复杂度:O(nlogn)。
它排序的范围是[first,last),包括first,不包括last。
sort可以用自定义的比较函数进行过排序,也可以用系统的四种排序:
less(升序)、greater(降序)、less_equal、greater_equal。
缺省情况下,程序按从小到大的顺序排序,less可以不写。
*/
//综合表达:
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <vector> //注:本电脑不支持万能头文件#include <bits/stdc++.h>,需要记住其他几个头文件
using namespace std;
bool my_less(int i, int j) {return (i < j);}
bool my_greater(int i,int j){return (i > j);}
int main(){
vector<int> a = {3,7,2,5,6,8,5,4};
sort(a.begin(),a.begin()+4); //对前4个排序,输出2 3 5 7 6 8 5 4
// sort(a.begin(),a.end()); //从小到大排序,输出2 3 4 5 5 6 7 8
// sort(a.begin(),a.end(),less<int>()); //输出2 3 4 5 5 6 7 8
// sort(a.begin(),a.end(),my_less); //自定义排序,输出2 3 4 5 5 6 7 8
// sort(a.begin(),a.end(),greater<int>()); //从大到小排序,输出8 7 6 5 5 4 3 2
// sort(a.begin(),a.end(),my_greater); //输出8 7 6 5 5 4 3 2
for(int i=0;i<a.size();i++){ //输出
cout<<a[i]<<" ";
}
cout<<endl;
return 0;
}
/*2.2 next_permutation():(见eg3)
next_permutation():求“下一个”排列组合(全排列)。
例如三个字符{a,b,c}组成的序列,next_permutation()能按字典序返回6个组合:
abc,acb,bac,bca,cab,cba。
*/
/*2.3 链表和list:(见eg4)
STL的list:双向链表。它的内存空间不必连续,通过指针来进行数的访问,高效率地在任意地方删除和插入,
插入和删除操作是常数时间。
list和vector的优缺点正好相反,它们的应用场景不同:
(1)vector:插入和删除操作少,随机访问元素频繁;
(2)list:插入和删除频繁,随机访问较少。
*/
/*2.4 stack:(见eg5)
例子 说明
a.push_back(100); 定义栈,Type为数据类型,如int、float、char等。
s.push(item); 把item放到栈顶(入栈)。
s.top(); 返回栈顶的元素,但不会删除。
s.pop(); 删除栈顶的元素,但不会返回(出栈)。
s.size(); 返回栈中元素的个数。
s.empty(); 检查栈是否为空,如果为空返回true,否则返回false。
*/
/*2.5 queue:(见eg7)
例子 说明
queue<Type>q; 定义栈,Type为数据类型,如int,float,char等
q.push(item); 把item放进队列
q.front(); 返回队首元素,但不会删除
q.pop(); 删除队首元素
q.back(); 返回队尾元素
q.size(); 返回元素个数
q.empty(); 检查队列是否为空
*/
/*2.6 vector:
①首先是最基础的数据访问功能:
top()
back()
②其次是简单的数据操作:
erase(int index,int size) 删除
insert(int index,int size) 插入
push_back(int value) 添加到数据后面
pop_back() 弹出最后一个数据
③还有一些自身属性操作:
数据大小size()重新整理内存单元resize(int size)
④最后是整体的操作:
clear() 清空
empty() 是否为空
功能 例子 说明
定义int型数字 vector<int>a; 默认初始化,a为空
vector<int>b(a); 用a定义b
vector<int>a(100); a有100个值为0的元素
vector<int>a(100,6); 100个值为6的元素
定义string型数组 vector<string>a(10,"null"); 10个值为null的元素
vector<string>vec(10,"hello"); 10个值为hello的元素
vector<string>b(a.begin(),a.end()); b是a的复制
定义结构型数组 struct point {int x,y;};
vector<point>a; a用来存坐标
功能 例子 说明
赋值 a.push_back(100); 在尾部添加元素
元素个数 int size = a.size(); 元素个数
是否为空 bool isEmpty = a.empty(); 判断是否为空
打印 cout<<a[0]<<endl; 打印第一个元素
中间插入 a.insert(a.begin()+i,k); 在第i个元素前面插入k
尾部插入 a.insert(a.end(),10,5); 尾部插入10个值为5的元素
尾部插入 a.push_back(8); 尾部插入值为8的元素
删除尾部 a.pop_back(); 删除末尾元素
删除区间 a.erase(a.begin()+i,a.begin()+j); 删除区间[i,j-1]的元素
删除元素 a.erase(a.begin()+2); 删除第3个元素
调整大小 a.resize(n); 数组大小变为n
清空 a.clear();
翻转 reverse(a.begin(),a.end()); 用函数reverse翻转数组
排序 sort(a.begin(),a.end()); 用函数sort排序,从小到大
*/
/*2.7 set:(见eg8)
set(集合)用二叉搜索树实现,集合中的每个元素只出现一次,且是排好序的。访问元素的时间复杂度是O(logn)的。
set和map在竞赛题中应用很广泛。特别是需要用二叉搜索树处理数据的题目,如果用set或map实现,能极大地简化代码。
例子 说明
set<Type>A; 定义
A.insert(item); 把item放进set
A.erase(item); 删除元素item
A.clear(); 清空set
A.empty(); 判断是否为空
A.size(); 返回元素个数
A.find(k); 返回一个迭代器,指向键值k
A.lower_bound(K); 返回一个迭代器,指向键值不小于k的第一个元素
A.upper_bound(); 返回一个迭代器,指向键值大于k的第一个元素
*/
/*2.8 迭代器:(见eg9)
定位:既能像指针一样访问数据,又能保证整体工程的稳定性。
实际作用:用于访问一个容器内的数据的指针。
具体实现:
返回第一个元素的迭代器begin()
返回容器末尾的迭代器end() 遵循左开右闭原则
*/
/*2.9 map:(见eg10)
map:关联容器,实现从键(key)到值(value)的映射。
map效率高的原因:用平衡二叉搜索树来存储和访问。
例如:有n个学生,每人有姓名name和学号id。给定一个学生的name,要求查找他的id。简单的做法是:
定义string name[n]和int id[n](可以放在一个结构体中)存储信息,然后在name[]中查找这个学生,
找到后输出他的id。
这样做的缺点是,需要搜索所有的name[],复杂度是O(n),效率很低。
利用STL中的map容器,可以快速地实现这个查找,复杂度是O(logn)。
*/
/*2.10 双端队列:
dequeue<int> q
访问队首元素,如例:q.front(),即最早被压入队列的元素。
访问队尾元素,如例:q.back(),即最后被压入队列的元素。
*/
/*2.11 优先队列:(见eg11)
priority_queue< > q[]; //<>中放的是所插入的数据类型,[]中放的是优先队列的维数即需要放入几个优先队列。
q.top(); //返回具有最高优先级的元素值,但不删除该元素
q.pop(); //删除最高优先级元素
q.push(item); //插入新元素
STL中,优先队列是用二叉堆来实现的,往队列中push入一个数或pop一个数,复杂度是O(logn)。
图论的Dijkstra算法的程序实现,用STL的优先队列能极大地简化代码。
*/
eg1. sort数组排序
/*A - 例1 sort数组排序
Description
给你n个整数,请按从大到小的顺序输出其中前m大的数。
Input
每组测试数据有两行,第一行有两个数n,m(0<n,m<1000000),第二行包含n个各不相同,且都处于
区间[-500000,500000]的整数。 //[注]:scanf速度是cin的十倍,对于很大的数在程序限时的情况下要用scanf
Output
对每组测试数据按从大到小的顺序输出前m大的数。
Sample Input
5 3
3 -35 92 213 -644
Sample Output
213 92 3
*/
#include <iostream>
#include <algorithm>
#include <cstdio>
//#include <bits/stdc++.h> //万能头文件
using namespace std;
bool cmp(int a,int b){ //定义一个排序规则函数
return a>b;
}
int a[1000010]; //①数组一定要适当开大,防止越界;②特别注意:大数组要定义为全局变量,不能定义在函数内。全局变量在静态存储区分配内存,局部变量是在栈上分配内存空间的。(c语言程序在运行时会动态创建一个堆栈段,里面存放着调用栈,保存着函数的调用关系和局部变量。)如果数组太大,可能会造成栈溢出。(定义在函数外的为全局变量,定义在函数内的为局部变量)
int main(){
int num1,num2;
while(cin>>num1>>num2){
for(int i=0;i<num1;i++){
scanf("%d",&a[i]);
}
sort(a,a+num1,cmp); //如果之前不定义一个判断函数cmp,sort()是默认按照升序排列的
for(int j=0;j<num2;j++){
cout<<a[j]<<" ";
}
cout<<endl;
}
return 0;
}
eg2. sort结构体排序
/*B - 例2 sort结构体排序
Description
将气球按照权值的从大到小进行排序
Input
第一行是一个整数T,表示用例号。
对于每种情况,第一行是一个整数n,这是问题的个数。
接下来有n行,每一行都有一个字符串和一个整数,在第i行中,字符串表示第i个问题的气球的颜色,
整数表示气球的权值。 //此时就需要用一个结构体同时记录颜色和权值,进行一个捆绑式的排序。
Output
对于每种情况,都需要输出一行。
一行中应该有n个字符串表示你选择的解决顺序。
请确保每两个字符串之间只有一个空格,没有多余的空格。
Sample Input
3
3
red 1
green 2
yellow 3
1
blue 83
2
red 2
white 1
Sample Output
yellow green red
blue
red white
*/
#include <iostream>
#include <string>
#include <algorithm>
#include <cstdio>
using namespace std;
struct ss{
string color;
int value;
}num[555];
bool cmp(ss a,ss b){ //定义排序规则
return a.value>b.value;
/*if(a.value==b.value){
return a.color>b.color;
}else{
return a.value>b.value;
}*/
}
int main(){
int t;
cin>>t;
while(t--){
int n;
cin>>n;
getchar();
for(int i=0;i<n;i++){
cin>>num[i].color;
cin>>num[i].value;
}
sort(num,num+n,cmp);
for(int i=0;i<n-1;i++){ //此处循环设置i<n-1,等最后输出num[n-1].color是为了最后一个颜色输出后不再输出一个空格。
//num[i].color;
cout<<num[i].color<<" ";
}
cout<<num[n-1].color<<endl;
//cout<<endl;
}
return 0;
}
eg3. 用next_permutation()输出“下一个”排列组合
/*C - 例3 用next_permutation()输出“下一个”排列组合 //法二:深度优先搜索。但需自己写代码。
Description
给定n个数字,从1到n,要求输出第m小的序列。
Input
由数字n和m组成,1 <= n <= 1000,1 <= m <= 10000.
Output
输出第m小的序列。
Sample Input
6 4
11 8
Sample Output
1 2 3 5 6 4
1 2 3 4 5 6 7 9 8 11 10
*/
#include <iostream>
#include <cstdio>
#include <algorithm>
using namespace std;
int main(){
int n,m;
while(cin>>n>>m){
int a[2000];
for(int i=0;i<n;i++){
a[i]=i+1;
}
int p;
p=0;
do{
p++;
if(p==m){
for(int i=0;i<n-1;i++){
cout<<a[i]<<" ";
}
cout<<a[n-1]<<endl;
break;
}
}while(next_permutation(a,a+n));
}
return 0;
}
eg4.链表应用
/*D - 例4
Description
某部队进行新兵队列训练,将新兵从一开始按顺序依次编号,并排成一行横队,训练的规则如下:
从头开始一至二报数,凡报到二的出列,剩下的向小序号方向靠拢,再从头开始进行一至三报数,凡报到三
的出列,剩下的向小序号方向靠拢,继续从头开始进行一至二报数...,以后从头开始轮流进行一至二报数、
一至三报数直到剩下的人数不超过三人为止。 //注意报数是按一至二全部报完一遍再从头一至三全部报完一遍...
Input
本题有多个测试数据组,第一行为组数N,接着为N行新兵人数,新兵人数不超过5000。
Output
共有N行,分别对应输入的新兵人数,每行输出剩下的新兵最初的编号,编号之间有一个空格。
Sample Input
2
20
40
Sample Output
1 7 19
1 19 37
*/
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <vector>
#include <string>
#include <cmath>
#include <list>
using namespace std;
int main(){
int t,n;
cin>>t;
while(t--){
cin>>n;
int k;
k=2;
list<int >blist;
list<int >::iterator it; //定义链表迭代器
for(int i=1;i<=n;i++){
blist.push_back(i);
}
while(blist.size()>3){
int num;
num=1;
for(it=blist.begin();it!=blist.end();){
if(num++%k==0){
it=blist.erase(it);
}else{
it++;
}
}
if(k==2){
k=3;
continue;
}else{
k=2;
continue;
}
//k==2?k=3:k=2;
}
for(it=blist.begin();it!=blist.end();it++){
if(it!=blist.begin()){
cout<<" ";
}
cout<<*it;
}
cout<<endl;
}
return 0;
}
eg5. 栈的应用
/*E - 例5 栈的应用
Description
输入测试例子数量t,然后输入t行字符串,将每一行的每一个单词逆序后输出该行的语句(字符串)
Sample Input
3
olleh !dlrow
m'I morf .udh
I ekil .mca
Sample Output
hello world!
I'm from hdu.
I like acm.
*/
#include <iostream>
#include <stack>
#include <string>
#include <cstdio>
#include <algorithm>
using namespace std;
int main(){
string s;
int t;
cin>>t;
getchar();
while(t--){
getline(cin,s);
int len;
len=(int )s.size();
stack<char >st;
for(int i=0;i<len;i++){
if(s[i]!=' '){
st.push(s[i]);
}
if(s[i]==' '||i==len-1){
while(!st.empty()){
printf("%c",st.top());
st.pop();
}
if(s[i]==' '){
cout<<" ";
}
}
}
cout<<endl;
}
return 0;
}
eg7. 栈和队列的应用
/*G - 例7 栈和队列的应用
Description
第一行是整数N(命令数),以及单词“FIFO”或“FILO”,FIFO表示“先进先出”,FILO表示“先进后出”
随后的N行,每行是“IN M”或“OUT”,对于每一个“OUT”输出对应的数值,没有数值输出“None”
Sample Input
4
4 FIFO
IN 1
IN 2
OUT
OUT
4 FILO
IN 1
IN 2
OUT
OUT
5 FIFO
IN 1
IN 2
OUT
OUT
OUT
5 FILO
IN 1
IN 2
OUT
IN 3
OUT
Sample Input
1
2
2
1
1
2
None
2
3
*/
#include <iostream>
#include <cstdio>
#include <queue>
#include <stack>
#include <string>
#include <algorithm>
using namespace std;
int main(){
int t;
cin>>t;
while(t--){
int n;
cin>>n;
string s1,s2;
cin>>s1;
if(s1=="FIFO"){
queue<int>qu;
while(n--){
cin>>s2;
if(s2=="IN"){
int num;
cin>>num;
qu.push(num);
}else{
if(qu.empty()){
cout<<"None"<<endl;
}else{
cout<<qu.front()<<endl;
qu.pop();
}
}
}
}else{
stack<int>st;
while(n--){
cin>>s2;
if(s2=="IN"){
int num;
cin>>num;
st.push(num);
}else{
if(st.empty()){
cout<<"None"<<endl;
}else{
cout<<st.top()<<endl;
st.pop();
}
}
}
}
}
return 0;
}
eg8. 产生冠军(集合set的应用)
/*H - 例8 产生冠军(集合set的应用)
Description
有一群人,打乒乓球比赛,两两捉对撕杀,每两个人之间最多打一场比赛。
球赛的规则如下:
如果A打败了B,B又打败了C,而A与C之间没有进行过比赛,那么就认定,A一定能打败C。
如果A打败了B,B又打败了C,而且,C又打败了A,那么A、B、C三者都不可能成为冠军。
根据这个规则,无需循环较量,或许就能确定冠军。你的任务就是面对一群比赛选手,在经过了若干场撕杀
之后,确定是否已经实际上产生了冠军。
Input
输入含有一些选手群,每群选手都以一个整数n(n<1000)开头,后跟n对选手的比赛结果,比赛结果以一对
选手名字(中间隔一空格)表示,前者战胜后者。如果n为0,则表示输入结束。
Output
对于每个选手群,若你判断出产生了冠军,则在一行中输出“Yes”,否则在一行中输出“No”。
Sample Input
3
Alice Bob
Smith John
Alice Smith
5
a c
c d
d e
b e
a d
0
Sample Output
Yes
No
*/
#include <iostream>
#include <string>
#include <set>
using namespace std;
int main(){
int t;
while(cin>>t){
if(t==0){
break;
}
set<string>st1,st2;
string s1,s2;
while(t--){
cin>>s1>>s2;
st1.insert(s1);
st1.insert(s2);
st2.insert(s2);
}
if(st1.size()-st2.size()!=1){
cout<<"No"<<endl;
}else{
cout<<"Yes"<<endl;
}
}
return 0;
}
eg9. 迭代器应用
/*I - 例9 迭代器应用
Description
参加过上个月月赛的同学一定还记得其中的一个最简单的题目,就是{A}+{B},那个题目求的是两个集合的
并集,今天我们这个A-B求的是两个集合的差,就是做集合的减法运算。(当然,大家都知道集合的定义,
就是同一个集合中不会有两个相同的元素,这里还是提醒大家一下)
Input
每组输入数据占1行,每行数据的开始是2个整数n(0<=n<=100)和m(0<=m<=100),分别表示集合A和集合B的
元素个数,然后紧跟着n+m个元素,前面n个元素属于集合A,其余的属于集合B. 每个元素为不超出int范围
的整数,元素之间有一个空格隔开.
如果n=0并且m=0表示输入的结束,不做处理。
Output
针对每组数据输出一行数据,表示A-B的结果,如果结果为空集合,则输出“NULL”,否则从小到大输出结果,
为了简化问题,每个元素后面跟一个空格.
Sample Input
3 3 1 2 3 1 4 7
3 7 2 5 8 2 3 4 5 6 7 8
0 0
Sample Output
2 3
NULL
*/
#include <iostream>
#include <set>
using namespace std;
int main(){
int num1,num2;
while(cin>>num1>>num2){
if(num1==num2&&num1==0){
break;
}
int num;
set<int>st;
for(int i=0;i<num1;i++){
cin>>num;
st.insert(num);
}
for(int i=0;i<num2;i++){
cin>>num;
if(st.find(num)!=st.end()){
st.erase(num);
}
}
if(st.size()==0){
cout<<"NULL"<<endl;
continue;
}
set<int >::iterator it;
for(it=st.begin();it!=st.end();it++){
cout<<*it<<" ";
}
cout<<endl;
}
return 0;
}
eg10.
/*J - 例10
Description
女孩dandelion经常去购物,她特别喜欢一家叫“memory”的商店。由于春节快到了,所有商店的价格每天都在上涨。她想知道这家商店每天的价格排名。
Input
第一行是数字n(n <= 10000),代表商店的数量。
后面n行,每行有一个字符串(长度小于31,只包含小写字母和大写字母)表示商店的名称。
然后一行是数字m(1 <= m <= 50),表示天数。
后面有m部分,每部分有n行,每行是数字s和一个字符串p,表示商店p在这一天涨价s。
Output
包含m行,第i行显示第i天后店铺“memory”的排名。排名的定义为:如果有t个商店的价格高于“memory”,
那么它的排名是t+1。
Sample Input
3
memory
kfc
wind
2
49 memory
49 kfc
48 wind
80 kfc
85 wind
83 memory
Sample Output
1
2
*/
#include <iostream>
#include <string>
#include <map>
using namespace std;
int main(){
int t;
while(cin>>t){
for(int i=0;i<t;i++){
string s;
cin>>s;
}
int n;
cin>>n;
map<string,int>shop;
while(n--){
for(int i=0;i<t;i++){
string na;
int p;
cin>>p;
cin>>na;
shop[na]+=p;
}
map<string, int>::iterator it;
int ans;
ans=1;
for(it=shop.begin();it!=shop.end();it++){
if(it->second>shop["memory"]){
ans++;
}
}
cout<<ans<<endl;
}
}
return 0;
}
eg11.
/*K - 例11
Description
看病要排队这个是地球人都知道的常识。
不过经过细心的0068的观察,他发现了医院里排队还是有讲究的。0068所去的医院有三个医生同时看病。
而看病的人病情有轻重,所以不能根据简单的先来先服务的原则。所以医院对每种病情规定了10种不同的
优先级。级别为10的优先权最高,级别为1的优先权最低。医生在看病时,则会在他的队伍里面选择一个
优先权最高的人进行诊治。如果遇到两个优先权一样的病人的话,则选择最早来排队的病人。
现在就请你帮助医院模拟这个看病过程。
Input
输入数据包含多组测试,请处理到文件结束。
每组数据第一行有一个正整数N(0<N<2000)表示发生事件的数目。
接下来有N行分别表示发生的事件。
一共有两种事件:
1:"IN A B",表示有一个拥有优先级B的病人要求医生A诊治。(0<A<=3,0<B<=10)
2:"OUT A",表示医生A进行了一次诊治,诊治完毕后,病人出院。(0<A<=3)
Output
对于每个"OUT A"事件,请在一行里面输出被诊治人的编号ID。如果该事件时无病人需要诊治,则输出"EMPTY"。
诊治人的编号ID的定义为:在一组测试中,"IN A B"事件发生第K次时,进来的病人ID即为K。从1开始编号。
Sample Input
7
IN 1 1
IN 1 2
OUT 1
OUT 2
IN 2 1
OUT 2
OUT 1
2
IN 1 1
OUT 1
Sample Output
2
EMPTY
3
1
1
*/
#include <iostream>
#include <queue>
#include <algorithm>
#include <cstdio>
#include <string>
using namespace std;
struct node{
int value,id;
bool operator <(const node &b)const{
if(value==b.value){
return id>b.id;
}else{
return value<b.value;
}
}
};
int main(){
int t;
while(cin>>t){
node pep;
priority_queue<node >q[10];
int timt;
timt=1;
string s;
while(t--){
cin>>s;
if(s=="IN"){
int a;
cin>>a>>pep.value;
pep.id=timt;
timt++;
q[a-1].push(pep);
}else{
int a;
cin>>a;
if(q[a-1].empty()){
cout<<"EMPTY"<<endl;
}else{
cout<<q[a-1].top().id<<endl;
q[a-1].pop();
}
}
}
}
return 0;
}