ACM竞赛常用STL(一)

本文详细介绍了C++ STL中的vector、string、stack、queue、map等容器及next_permutation、pair等算法函数,包括定义、使用方法、基本操作和应用场景。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

全排列函数next_permutation

STL 中专门用于排列的函数(可以处理存在重复数据集的排列问题)

头文件:#include <algorithm>

using namespace std;

调用: next_permutation(start, end);

注意:函数要求输入的是一个升序排列的序列的头指针和尾指针.

用法:

 

[cpp]  view plain copy
  1. // 数组  
  2. int a[N];  
  3. sort(a, a+N);  
  4. next_permutation(a, a+N);  
  5. // 向量  
  6. vector<int> ivec;  
  7. sort(ivec.begin(), ivec.end());  
  8. next_permutation(ivec.begin(), ivec.end());  
  9. 例子:  
  10. vector<int> myVec;  
  11. // 初始化代码  
  12. sort(myVec.begin(),myVec.end());  
  13. do{  
  14.     for (i = 0 ;i < size;i ++ ) cout << myVec[i] << " \t " ;  
  15.     cout << endl;  
  16. }while (next_permutation(myVec.begin(), myVec.end()));  


ACM/ICPC 竞赛之STL--pair

STL <utility>头文件中描述了一个看上去非常简单的模板类pair,用来表示一个二元组或元素对,并提供了按照字典序对元素对进行大小比较的比较运算符模板函数。

例如,想要定义一个对象表示一个平面坐标点,则可以:

pair<double, double> p1;cin >> p1.first >> p1.second;pair 模板类需要两个参数:首元素的数据类型和尾元素的数据类型。pair 模板类对象有两个成员:first second,分别表示首元素和尾元素。

<utility>中已经定义了pair 上的六个比较运算符:<><=>===!=,其规则是先比较firstfirst 相等时再比较second,这符合大多数应用的逻辑。当然,也可以通过重载这几个运算符来重新指定自己的比较逻辑。除了直接定义一个pair 对象外,如果需要即时生成一个pair 对象,也可以调用在<utility>中定义的一个模板函数:make_pairmake_pair 需要两个参数,分别为元素对的首元素和尾元素。

在题1067--Ugly Numbers 中,就可以用pair 来表示推演树上的结点,用first 表示结点的值,用second 表示结点是由父结点乘以哪一个因子得到的。

[cpp]  view plain copy
  1. #include <iostream>  
  2. #include <queue>  
  3. using namespace std;  
  4. typedef pair<unsigned longint> node_type;  
  5. int main()  
  6. {  
  7.     unsigned long result[1500];  
  8.     priority_queue< node_type, vector<node_type>,  
  9.     greater<node_type> > Q;  
  10.     Q.push( make_pair(1, 2) );  
  11.     for (int i=0; i<1500; i++)  
  12.     {  
  13.         node_type node = Q.top(); Q.pop();  
  14.         switch(node.second)  
  15.         {  
  16.             case 2: Q.push( make_pair(node.first*2, 2) );  
  17.             case 3: Q.push( make_pair(node.first*3, 3) );  
  18.             case 5: Q.push( make_pair(node.first*5, 5) );  
  19.         }  
  20.         result[i] = node.first;  
  21.     }  
  22.     int n;  
  23.     cin >> n;  
  24.     while (n>0)  
  25.     {  
  26.         cout << result[n-1] << endl;  
  27.         cin >> n;  
  28.     }  
  29.     return 0;  
  30. }  


ACM/ICPC 竞赛之STL--vector

STL <vector>头文件中定义了vector(向量容器模板类),vector容器以连续数组的方式存储元素序列,可以将vector 看作是以顺序结构实现的线性表。当我们在程序中需要使用动态数组时,vector 将会是理想的选择,vector 可以在使用过程中动态地增长存储空间。

vector 模板类需要两个模板参数,第一个参数是存储元素的数据类型,第二个参数是存储分配器的类型,其中第二个参数是可选的,如果不给出第二个参数,将使用默认的分配器。

下面给出几个常用的定义vector 向量对象的方法示例:38

vector<int> s;

定义一个空的vector 对象,存储的是int 类型的元素。

vector<int> s(n);定义一个含有int 元素的vector 对象。

vector<int> s(first, last);定义一个vector 对象,并从由迭代器first last 定义的序列[first,last)中复制初值。

vector 的基本操作有:

s[i]直接以下标方式访问容器中的元素。

s.front() 返回首元素。

s.back() 返回尾元素。

s.push_back(x)向表尾插入元素x

s.size() 返回表长。

s.empty() 当表空时,返回真,否则返回假。

s.pop_back() 删除表尾元素。

s.begin() 返回指向首元素的随机存取迭代器。

s.end() 返回指向尾元素的下一个位置的随机存取迭代器。

s.insert(it, x) 向迭代器it 指向的元素前插入新元素val

s.insert(it, n, x)向迭代器it 指向的元素前插入x

s.insert(it, first, last)将由迭代器first last 所指定的序列[first, last)插入到迭代器it

指向的元素前面。

s.erase(it)删除由迭代器it 所指向的元素。

s.erase(first, last)删除由迭代器first last 所指定的序列[first, last)

s.reserve(n)预分配缓冲空间,使存储空间至少可容纳个元素。

s.resize(n)改变序列的长度,超出的元素将会被删除,如果序列需要扩展(原空间小于n),元素默认值将填满扩展出的空间。

s.resize(n, val)改变序列的长度,超出的元素将会被删除,如果序列需要扩展(原空间小于n),将用val 填满扩展出的空间。

s.clear()删除容器中的所有的元素。

s.swap(v)将与另一个vector 对象进行交换。

s.assign(first, last)将序列替换成由迭代器first last 所指定的序列[first, last)[first, last)不能是原序列中的一部分。要注意的是,resize 操作和clear 操作都是对表的有效元素进行的操作,但并不一定会改变缓冲空间的大小。另外,vector 还有其他一些操作如反转、取反等,不再一下列举。vector 上还定义了序列之间的比较操作运算符(>, <, >=, <=, ==, !=)

可以按照字典序比较两个序列。还是来看一些示例代码。输入个数不定的一组整数,再将这组整数按倒序输出,

如下所示:

[cpp]  view plain copy
  1. #include <iostream>  
  2. #include <vector>  
  3. using namespace std;  
  4. int main()  
  5. {  
  6.     vector<int> L;  
  7.     int x;  
  8.     while (cin>>x) L.push_back(x);  
  9.     for (int i=L.size()-1; i>=0; i--)   
  10.         cout << L[i] << " ";  
  11.     cout << endl;  
  12.     return 0;  
  13. }  


ACM/ICPC 竞赛之STL--iterator 简介

iterator(迭代器)是用于访问容器中元素的指示器,从这个意义上说,iterator(迭代器)相当于数据结构中所说的“遍历指针”,也可以把iterator(迭代器)看作是一种泛化的指针。STL 中关于iterator(迭代器)的实现是相当复杂的,这里我们暂时不去详细讨论关于iterator(迭代器)的实现和使用,而只对iterator(迭代器)做一点简单的介绍。

简单地说,STL 中有以下几类iterator(迭代器)

输入iterator(迭代器),在容器的连续区间内向前移动,可以读取容器内任意值;输出iterator(迭代器),把值写进它所指向的容器中;前向iterator(迭代器),读取队列中的值,并可以向前移动到下一位置(++p,p++);双向iterator(迭代器),读取队列中的值,并可以向前向后遍历容器;随机访问iterator(迭代器), 可以直接以下标方式对容器进行访问,vector iterator(迭代器)就是这种iterator(迭代器);流iterator(迭代器),可以直接输出、输入流中的值;每种STL 容器都有自己的iterator(迭代器)子类,下面先来看一段简单的示例代码:

[cpp]  view plain copy
  1. #include <iostream>  
  2. #include <vector>  
  3. using namespace std;  
  4. main()  
  5. {  
  6.     vector<int> s;  
  7.     for (int i=0; i<10; i++)   
  8.         s.push_back(i);  
  9.     for (vector<int>::iterator it=s.begin(); it!=s.end();it++)  
  10.         cout << *it << " ";  
  11.     cout << endl;  
  12.     return 1;  
  13. }  


vector begin()end()方法都会返回一个vector::iterator 对象,分别指向vector 的首元素位置和尾元素的下一个位置(我们可以称之为结束标志位置)。对一个iterator(迭代器)对象的使用与一个指针变量的使用极为相似,或者可以这样说,指针就是一个非常标准的iterator(迭代器)。再来看一段稍微特别一点的代码:

[cpp]  view plain copy
  1. #include <iostream>  
  2. #include <vector>  
  3. using namespace std;  
  4. main()  
  5. {  
  6.     vector<int> s;  
  7.     s.push_back(1);  
  8.     s.push_back(2);  
  9.     s.push_back(3);  
  10.     copy(s.begin(), s.end(), ostream_iterator<int>(cout, ""));  
  11.     cout <<endl;  
  12.     return 1;  
  13. }  


这段代码中的copy 就是STL 中定义的一个模板函数,copy(s.begin(),s.end(), ostream_iterator<int>(cout, " "));的意思是将由s.begin()s.end()(不含s.end())所指定的序列复制到标准输出流cout 中,用" "作为每个元素的间隔。也就是说,这句话的作用其实就是将表中的所有内容依次输出。iterator(迭代器)STL 容器和算法之间的“胶合剂”,几乎所有的STL 算法都是通过容器的iterator(迭代器)来访问容器内容的。只有通过有效地运用iterator(迭代器),才能够有效地运用STL 强大的算法功能。

ACM/ICPC 竞赛之STL--string

字符串是程序中经常要表达和处理的数据,我们通常是采用字符数组或字符指针来表示字符串。STL 为我们提供了另一种使用起来更为便捷的字符串的表达方式:stringstring 类的定义在头文件<string>中。string 类其实可以看作是一个字符的vectorvector 上的各种操作都可以适用于string,另外,string 类对象还支持字符串的拼合、转换等操作。下面先来看一个简单的例子:

[cpp]  view plain copy
  1. #include <iostream>  
  2. #include <string>  
  3. using namespace std;  
  4. int main()  
  5. {  
  6.     string s = "Hello! ", name;  
  7.     cin >> name;  
  8.     s += name;  
  9.     s += '!';  
  10.     cout << s << endl;  
  11.     return 0;  
  12. }  


再以题1064--Parencoding 为例,看一段用string 作为容器,实现由P

代码还原括号字符串的示例代码片段:

int m;

cin >> m; // P 编码的长度

string str; // 用来存放还原出来的括号字符串

int leftpa = 0; // 记录已出现的左括号的总数

for (int j=0; j<m; j++){

int p;

cin >> p;

for (int k=0; k<p-leftpa; k++) str += '(';

str += ')';

leftpa = p;

}

ACM/ICPC 竞赛之STL--stack/queue

stack()queue(队列)也是在程序设计中经常会用到的数据容器,STL为我们提供了方便的stack()queue(队列)的实现。39准确地说,STL 中的stack queue 不同于vectorlist 等容器,而是对这些容器的重新包装。这里我们不去深入讨论STL stack queue 的实现细节,而是来了解一些他们的基本使用。

1stack

stack 模板类的定义在<stack>头文件中。

stack 模板类需要两个模板参数,一个是元素类型,一个容器类型,但只有元

素类型是必要的,在不指定容器类型时,默认的容器类型为deque

定义stack 对象的示例代码如下:

stack<int> s1;

stack<string> s2;

stack 的基本操作有:

入栈,如例:s.push(x);

出栈,如例:s.pop();注意,出栈操作只是删除栈顶元素,并不返回该元素。

访问栈顶,如例:s.top()

判断栈空,如例:s.empty(),当栈空时,返回true

访问栈中的元素个数,如例:s.size()

下面是用string stack 写的解题1064--Parencoding 的程序。

[cpp]  view plain copy
  1. #include <iostream>  
  2. #include <string>  
  3. #include <stack>  
  4. using namespace std;  
  5. int main()  
  6. {  
  7.     int n;  
  8.     cin >> n;  
  9.     for (int i=0; i<n; i++)  
  10.     {  
  11.         int m;  
  12.         cin >> m;  
  13.         string str;  
  14.         int leftpa = 0;  
  15.         for (int j=0; j<m; j++) // 读入P 编码,构造括号字符串  
  16.         {    
  17.             int p;  
  18.             cin >> p;  
  19.             for (int k=0; k<p-leftpa; k++)   
  20.                 str += '(';  
  21.                 str += ')';  
  22.                 leftpa = p;  
  23.         }  
  24.         stack<int> s;  
  25.         for (string::iterator it=str.begin();it!=str.end(); it++)   
  26.         { // 构造M 编码  
  27.             if (*it=='(') s.push(1);  
  28.             else  
  29.             {  
  30.                 int p = s.top(); s.pop();  
  31.                 cout << p << " ";  
  32.                 if (!s.empty()) s.top() += p;  
  33.             }  
  34.         }  
  35.         cout << endl;  
  36.     }  
  37.     return 0;  
  38. }  


2queue

queue 模板类的定义在<queue>头文件中。stack 模板类很相似,queue 模板类也需要两个模板参数,一个是元素类型,一个容器类型,元素类型是必要的,容器类型是可选的,默认为deque 类型。

定义queue 对象的示例代码如下:

queue<int> q1;

queue<double> q2;

queue 的基本操作有:

入队,如例:q.push(x); 接到队列的末端。

出队,如例:q.pop(); 弹出队列的第一个元素,注意,并不会返回被弹出元素的值。

访问队首元素,如例:q.front(),即最早被压入队列的元素。

访问队尾元素,如例:q.back(),即最后被压入队列的元素。

判断队列空,如例:q.empty(),当队列空时,返回true

访问队列中的元素个数,如例:q.size()

3priority_queue

<queue>头文件中,还定义了另一个非常有用的模板类priority_queue(优先队列)。优先队列与队列的差别在于优先队列不是按照入队的顺序出队,而是按照队列中元素的优先权顺序出队(默认为大者优先,也可以通过指定算子来指定自己的优先顺序)。priority_queue 模板类有三个模板参数,第一个是元素类型,第二个容器类型,第三个是比较算子。其中后两个都可以省略,默认容器为vector,默认算子为less,即小的往前排,大的往后排(出队时序列尾的元素出队)。

定义priority_queue 对象的示例代码如下:

priority_queue<int> q1;

priority_queue< pair<int, int> > q2; // 注意在两个尖括号之间一定要留空格。

priority_queue<int, vector<int>, greater<int> > q3; // 定义小的先出队

priority_queue 的基本操作与queue 相同。

初学者在使用priority_queue 时,最困难的可能就是如何定义比较算子了。如果是基本数据类型,或已定义了比较运算符的类,可以直接用STL less算子和greater 算子——默认为使用less 算子,即小的往前排,大的先出队。如果要定义自己的比较算子,方法有多种,这里介绍其中的一种:重载比较运算符。优先队列试图将两个元素代入比较运算符(less 算子,调用x<y,对greater 算子,调用x>y),若结果为真,则排在前面,将先于出队,反之,则将排在前面,将先出队。

看下面这个简单的示例:

[cpp]  view plain copy
  1. #include <iostream>  
  2. #include <queue>  
  3. using namespace std;  
  4. class T  
  5. {  
  6.     public:  
  7.         int x, y, z;  
  8.         T(int a, int b, int c):x(a), y(b), z(c){}  
  9. };  
  10. bool operator < (const T &t1, const T &t2)  
  11. {  
  12.     return t1.z < t2.z; // 按照z 的顺序来决定t1 和t2 的顺序  
  13. }  
  14. int main()  
  15. {  
  16.     priority_queue<T> q;  
  17.     q.push(T(4,4,3));  
  18.     q.push(T(2,2,5));  
  19.     q.push(T(1,5,4));  
  20.     q.push(T(3,3,6));  
  21.     while (!q.empty())  
  22.     {  
  23.         T t = q.top(); q.pop();  
  24.         cout << t.x << " " << t.y << " " << t.z << endl;  
  25.     }  
  26. return 0;   
  27. }  
  28. /*输出结果为(注意是按照z 的顺序从大到小出队的): 
  29. 3 3 6 
  30. 2 2 5 
  31. 1 5 4 
  32. 4 4 3*/  
  33.   
  34.   
  35. //再看一个按照z 的顺序从小到大出队的例子:  
  36. #include <iostream>  
  37. #include <queue>  
  38. using namespace std;  
  39. class T  
  40. {  
  41.     public:  
  42.         int x, y, z;  
  43.     T(int a, int b, int c):x(a), y(b), z(c)  
  44.     {  
  45.     }  
  46. };  
  47. bool operator > (const T &t1, const T &t2)  
  48. {  
  49.     return t1.z > t2.z;  
  50. }  
  51. int main()  
  52. {  
  53.     priority_queue<T, vector<T>, greater<T> > q;  
  54.     q.push(T(4,4,3));  
  55.     q.push(T(2,2,5));  
  56.     q.push(T(1,5,4));  
  57.     q.push(T(3,3,6));  
  58.     while (!q.empty())  
  59.     {  
  60.         T t = q.top(); q.pop();  
  61.         cout << t.x << " " << t.y << " " << t.z << endl;  
  62.     }  
  63.     return 0;  
  64. }  


输出结果为:

4 4 3

1 5 4

2 2 5

3 3 6

如果我们把第一个例子中的比较运算符重载为:

bool operator < (const T &t1, const T &t2){

return t1.z > t2.z; // 按照的顺序来决定t1 t2 的顺序

}

则第一个例子的程序会得到和第二个例子的程序相同的输出结果。

再回顾一下用优先队列实现的题1067--Ugly Numbers 的代码:

[cpp]  view plain copy
  1. #include <iostream>  
  2. #include <queue>  
  3. using namespace std;  
  4. typedef pair<unsigned long intint> node_type;  
  5. int main( int argc, char *argv[] )  
  6. {  
  7.     unsigned long int result[1500];  
  8.     priority_queue< node_type, vector<node_type>,  
  9.     greater<node_type> > Q;  
  10.     Q.push( make_pair(1, 3) );  
  11.     for (int i=0; i<1500; i++)  
  12.     {  
  13.         node_type node = Q.top();  
  14.         Q.pop();  
  15.         switch(node.second)  
  16.         {  
  17.             case 3: Q.push( make_pair(node.first*2, 3) );  
  18.             case 2: Q.push( make_pair(node.first*3, 2) );  
  19.             case 1: Q.push( make_pair(node.first*5, 1) );  
  20.         }  
  21.         result[i] = node.first;  
  22.     }  
  23.     int n;  
  24.     cin >> n;  
  25.     while (n>0)  
  26.     {  
  27.         cout << result[n-1] << endl;  
  28.         cin >> n;  
  29.     }  
  30.     return 1;  
  31. }  


ACM/ICPC 竞赛之STL--map

STL 的头文件<map>中定义了模板类map multimap,用有序二叉树来存贮类型为pair<const Key, T>的元素对序列。序列中的元素以const Key部分作为标识,map 中所有元素的Key 值都必须是唯一的,multimap 则允许有重复的Key 值。可以将map 看作是由Key 标识元素的元素集合,这类容器也被称为“关联容器”,可以通过一个Key 值来快速确定一个元素,因此非常适合于需要按照Key值查找元素的容器。map 模板类需要四个模板参数,第一个是键值类型,第二个是元素类型,第三个是比较算子,第四个是分配器类型。其中键值类型和元素类型是必要的。map 的基本操作有:

1、定义map 对象,例如:

map<string, int> m;

2、向map 中插入元素对,有多种方法,例如:

m[key] = value;

[key]操作是map 很有特色的操作,如果在map 中存在键值为key 的元素对,

则返回该元素对的值域部分,否则将会创建一个键值为key 的元素对,值域为默认值。所以可以用该操作向map 中插入元素对或修改已经存在的元素对的值域部分。

m.insert( make_pair(key, value) );

也可以直接调用insert 方法插入元素对,insert 操作会返回一个pair,当map 中没有与key 相匹配的键值时,其first 是指向插入元素对的迭代器,其second true;若map 中已经存在与key 相等的键值时,其first 是指向该元素对的迭代器,second false

3、查找元素对,例如:

int i = m[key];

要注意的是,当与该键值相匹配的元素对不存在时,会创建键值为key 的元素对。map<string, int>::iterator it = m.find(key);如果map 中存在与key 相匹配的键值时,find 操作将返回指向该元素对的迭代器,否则,返回的迭代器等于map end()(参见vector 中提到的begin

end 操作)。

4、删除元素对,例如:

m.erase(key);删除与指定key 键值相匹配的元素对,并返回被删除的元素的个数。

m.erase(it);删除由迭代器it 所指定的元素对,并返回指向下一个元素对的迭代器。

看一段简单的示例代码:

[cpp]  view plain copy
  1. #include<map>  
  2. #include<iostream>  
  3. using namespace std;  
  4. typedef map<int, string, less<int> > M_TYPE;  
  5. typedef M_TYPE::iterator M_IT;  
  6. typedef M_TYPE::const_iterator M_CIT;  
  7. int main()  
  8. {  
  9.     M_TYPE MyTestMap;  
  10.     MyTestMap[3] = "No.3";  
  11.     MyTestMap[5] = "No.5";  
  12.     MyTestMap[1] = "No.1";  
  13.     MyTestMap[2] = "No.2";  
  14.     MyTestMap[4] = "No.4";  
  15.     M_IT it_stop = MyTestMap.find(2);  
  16.     cout << "MyTestMap[2] = " << it_stop->second << endl;  
  17.     it_stop->second = "No.2 After modification";  
  18.     cout << "MyTestMap[2] = " << it_stop->second << endl;  
  19.     cout << "Map contents : " << endl;  
  20.     for(M_CIT it = MyTestMap.begin(); it != MyTestMap.end();it++)  
  21.     {  
  22.         cout << it->second << endl;  
  23.     }  
  24.     return 0;  
  25. }  
  26. /*程序执行的输出结果为: 
  27. MyTestMap[2] = No.2 
  28. MyTestMap[2] = No.2 After modification 
  29. Map contents : 
  30. No.1 
  31. No.2 After modification 
  32. No.3 
  33. No.4 
  34. No.5*/  


再看一段简单的示例代码:

[cpp]  view plain copy
  1. #include <iostream>  
  2. #include <map>  
  3. using namespace std;  
  4. int main()  
  5. {  
  6.     map<string, int> m;  
  7.     m["one"] = 1;  
  8.     m["two"] = 2;  
  9.     // 几种不同的insert 调用方法  
  10.     m.insert(make_pair("three", 3));  
  11.     m.insert(map<string, int>::value_type("four", 4));  
  12.     m.insert(pair<string, int>("five", 5));  
  13.     string key;  
  14.     while (cin>>key)  
  15.     {  
  16.         map<string, int>::iterator it = m.find(key);  
  17.         if (it==m.end())  
  18.         {  
  19.             cout << "No such key!" << endl;  
  20.         }  
  21.         else  
  22.         {  
  23.             cout << key << " is " << it->second << endl;  
  24.             cout << "Erased " << m.erase(key) << endl;  
  25.         }  
  26.     }  
  27.     return 0;  
  28. }  

由于STL--algorithm  另加一篇详解

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值