ACM競賽經常使用STL(一)

全排列函數next_permutation

STL 中專門用於排列的函數(能夠處理存在重複數據集的排列問題)node

頭文件:#include <algorithm>ios

using namespace std;算法

調用: next_permutation(start, end);數組

注意:函數要求輸入的是一個升序排列的序列的頭指針和尾指針.數據結構

用法:less

 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,分別表示首元素和尾元素。spa

在<utility>中已經定義了pair 上的六個比較運算符:<、>、<=、>=、==、!=,其規則是先比較first,first 相等時再比較second,這符合大多數應用的邏輯。固然,也能夠經過重載這幾個運算符來從新指定本身的比較邏輯。除了直接定義一個pair 對象外,若是須要即時生成一個pair 對象,也能夠調用在<utility>中定義的一個模板函數:make_pair。make_pair 須要兩個參數,分別爲元素對的首元素和尾元素。設計

在題1067--Ugly Numbers 中,就能夠用pair 來表示推演樹上的結點,用first 表示結點的值,用second 表示結點是由父結點乘以哪個因子獲得的。

 1 #include <iostream>
 2 #include <queue>
 3 using namespace std;
 4 typedef pair<unsigned long, int> 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);定義一個含有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 指向的元素前插入n 個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)預分配緩衝空間,使存儲空間至少可容納n 個元素。

s.resize(n)改變序列的長度,超出的元素將會被刪除,若是序列須要擴展(原空間小於n),元素默認值將填滿擴展出的空間。

s.resize(n, val)改變序列的長度,超出的元素將會被刪除,若是序列須要擴展(原空間小於n),將用val 填滿擴展出的空間。

s.clear()刪除容器中的全部的元素。

s.swap(v)將s 與另外一個vector 對象v 進行交換。

s.assign(first, last)將序列替換成由迭代器first 和last 所指定的序列[first, last)。[first, last)不能是原序列中的一部分。要注意的是,resize 操做和clear 操做都是對錶的有效元素進行的操做,但並不必定會改變緩衝空間的大小。另外,vector 還有其餘一些操做如反轉、取反等,再也不一下列舉。vector 上還定義了序列之間的比較操做運算符(>, <, >=, <=, ==, !=),

能夠按照字典序比較兩個序列。仍是來看一些示例代碼。輸入個數不定的一組整數,再將這組整數按倒序輸出,

以下所示:

 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(迭代器)子類,下面先來看一段簡單的示例代碼:

 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(迭代器)。再來看一段稍微特別一點的代碼:

 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 爲咱們提供了另外一種使用起來更爲便捷的字符串的表達方式:string。string 類的定義在頭文件<string>中。string 類其實能夠看做是一個字符的vector,vector 上的各類操做均可以適用於string,另外,string 類對象還支持字符串的拼合、轉換等操做。下面先來看一個簡單的例子:

 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

代碼還原括號字符串的示例代碼片斷:

 1 int m;
 2 
 3 cin >> m; // P 編碼的長度
 4 
 5 string str; // 用來存放還原出來的括號字符串
 6 
 7 int leftpa = 0; // 記錄已出現的左括號的總數
 8 
 9 for (int j=0; j<m; j++)
10 {
11 
12     int p;
13 
14     cin >> p;
15 
16     for (int k=0; k<p-leftpa; k++) str += '(';
17 
18     str += ')';
19 
20     leftpa = p;
21 
22 }

 

ACM/ICPC 競賽之STL--stack/queue

 

stack(棧)和queue(隊列)也是在程序設計中常常會用到的數據容器,STL爲咱們提供了方便的stack(棧)的queue(隊列)的實現。39準確地說,STL 中的stack 和queue 不一樣於vector、list 等容器,而是對這些容器的從新包裝。這裏咱們不去深刻討論STL 的stack 和queue 的實現細節,而是來了解一些他們的基本使用。

一、stack

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 的程序。

 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 }

 

二、queue

queue 模板類的定義在<queue>頭文件中。stack 模板類很類似,queue 模板類也須要兩個模板參數,一個是元素類型,一個容器類型,元素類型是必要的,容器類型是可選的,默認爲deque 類型。

定義queue 對象的示例代碼以下:

queue<int> q1;

queue<double> q2;

queue 的基本操做有:

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

出隊,如例:q.pop(); 彈出隊列的第一個元素,注意,並不會返回被彈出元素的值。

訪問隊首元素,如例:q.front(),即最先被壓入隊列的元素。

訪問隊尾元素,如例:q.back(),即最後被壓入隊列的元素。

判斷隊列空,如例:q.empty(),當隊列空時,返回true。

訪問隊列中的元素個數,如例:q.size()

三、priority_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 算子,即小的往前排,大的先出隊。若是要定義本身的比較算子,方法有多種,這裏介紹其中的一種:重載比較運算符。優先隊列試圖將兩個元素x 和y 代入比較運算符(對less 算子,調用x<y,對greater 算子,調用x>y),若結果爲真,則x 排在y 前面,y 將先於x 出隊,反之,則將y 排在x 前面,x 將先出隊。

看下面這個簡單的示例:

 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; // 按照z 的順序來決定t1 和t2 的順序

}

則第一個例子的程序會獲得和第二個例子的程序相同的輸出結果。

再回顧一下用優先隊列實現的題1067--Ugly Numbers 的代碼:

 1 #include <iostream>
 2 #include <queue>
 3 using namespace std;
 4 typedef pair<unsigned long int, int> 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 的基本操做有:

一、定義map 對象,例如:

map<string, int> m;

二、向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。

三、查找元素對,例如:

int i = m[key];

要注意的是,當與該鍵值相匹配的元素對不存在時,會建立鍵值爲key 的元素對。map<string, int>::iterator it = m.find(key);若是map 中存在與key 相匹配的鍵值時,find 操做將返回指向該元素對的迭代器,不然,返回的迭代器等於map 的end()(參見vector 中提到的begin

和end 操做)。

四、刪除元素對,例如:

m.erase(key);刪除與指定key 鍵值相匹配的元素對,並返回被刪除的元素的個數。

m.erase(it);刪除由迭代器it 所指定的元素對,並返回指向下一個元素對的迭代器。

看一段簡單的示例代碼:

 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*/

再看一段簡單的示例代碼:

 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 }
相關文章
相關標籤/搜索