演算法競賽入門經典_5 c++與STL入門
直接跳到第五章了
c語言是一門很有用的語言,但在演算法競賽中卻不流行,原因在於它太底層,缺少一些實用的東西。
下面是一個簡單的c++框架
#include<cstdio> int main() { int a, b; while(scanf("%d%d", &a, &b) == 2) printf("%d\n", a+b); return 0; }
注:其中c++中的標頭檔案使用sctdio代替stdio.h,cstring代替string.h,cmath代替math.h,cctype代替ctype.h
執行效果
下面是更復雜的:
#include <iostream> #include <algorithm> using namespace std; const int maxn = 100 + 10; int A[maxn]; int main() { // long long a, b; // _int64 a, b; int a, b; while(cin >> a >> b) { //使用_MIN代替min,因為algorithm中沒有min函式 cout <<_MIN(a, b) <<endl; } return 0; }
注:在vc6++不支援long long 型別,但是支援_int64的宣告,不過在cin>>a>>b中會報錯,因為不支援
執行效果
c++還支援引用型別
#include <iostream> using namespace std; void swap2(int &a, int &b) { int t = a; a = b; b = t; } int main() { int a = 3, b = 4; swap2(a, b); cout<< a << " "<< b << endl; return 0; }
執行效果:
c++中對字串的處理
#include <iostream> #include <string> #include <sstream> using namespace std; int main() { string line; while(getline(cin, line)) { int sum = 0; int x; stringstream ss(line); while(ss>>x) sum= sum + x; cout << sum << endl; } return 0; }
執行效果
c++中結構體
#include <iostream> using namespace std; struct Point{ int x, y; Point(int x=0, int y=0 ):x(x),y(y){} }; Point operator + (const Point &A, const Point &B){ return Point(A.x + B.x, A.y + B.y); } ostream& operator << (ostream &out, const Point &p){ out<< "("<< p.x << "," <<p.y<< ")"; return out; } int main() { Point a, b(1,2); a.x = 3; cout << a + b<<endl; return 0; }
首先,c++不需要使用typedef來定義一個struct了,而且在struct中,除了可以有變數,還可以有函式
其中,:x(x),y(y)是“Point {int x =0, int y = 0}{this->x = x;this->y = y;}”的簡寫
此外,上面程式碼還使用了過載運算子
執行結果:
c++模板的使用:
#include<iostream> using namespace std; template<typename T> T sum(T *begin, T *end) { T *p = begin; T ans = 0; for(; p != end; p++) ans = ans + *p; return ans; } int main() { double a[] = {1.1, 2.2, 3.3, 4.4}; cout << sum(a , a+4) << endl; return 0; }
其中:使用template <typename T>宣告一個模板T
執行效果:
c++的sort排序
題目:
/* 大理石在哪兒問題 現有N個大理石,每個大理石上寫了一個非負整數。首先把個數從小到大排序,然後回答 Q個問題,每個問題問是否有一個大理石寫著某個整數x,如果是,還要回答哪個石上寫 著x.排序後的大理石從左到右編號為1-N。 輸入: 4 1 2 3 5 1 5 5 2 1 3 3 3 1 2 3 輸出: CASE# 1: 5 found at 4 CASE# 2: 2 not found 3 found at 3 */
程式碼:
#include<algorithm> #include<iostream> using namespace std; const int maxn = 10000; int main() { int n, q, x, a[maxn], kase = 0; while(scanf("%d%d", &n, &q) == 2 && n){ printf("CASE# %d:\n", ++kase); for(int i = 0; i < n; i++) cin>>a[i]; sort(a, a+n); while(q--){ scanf("%d", &x); int p = lower_bound(a, a+n, x) - a; if(a[p] == x) printf("%d found at %d\n", x, p+1); else printf("%d not found\n", x); } } return 0; }
分析:sort函式可以對任意物件進行排序,引數1是陣列起始地址,引數2是陣列結尾地址,當然也可以定義自己的比較函式。
lower_bound函式是查詢大於或等於x的第一個位置。引數1,2分別是起始和結束地址,引數3是待查數字x.
unique函式可以刪除有序陣列中的重複元素。
執行效果:
c++中的可變陣列vector
問題:
/* 木塊問題 從左到右有n個木塊,編號為0--n-1,要求模擬以下4種操作(a和b都是編號) move a onto b; 把a和b上方的木塊全部歸位,然後把a摞在b上面。 move a over b: 把a上方的木塊全部歸位,然後把a放在b所在木塊的的頂部 pile a onto b: 把b上方的木塊全部歸位,然後把a和a上面的木塊整體摞在b上面 pile a over b:把a及上面的木塊整體摞在b上方的堆的頂部 */
程式碼:
#include <iostream> #include <algorithm> #include <string> #include <vector> using namespace std; const int maxn = 30; int n; vector<int> pile[maxn];//每個pile[i]是一個vector //找木塊a所在的pile和height,以引用的形式返回撥用者 void find_block(int a, int &p, int &h) { for(p = 0; p < n;p++) for(h = 0; h < pile[p].size(); h++) if(pile[p][h] == a)return; } //把第p堆高度為h的木塊上方的所有木塊移回原位 void clear_above(int p, int h){ for(int i= h+1; i < pile[p].size(); i++) { int b = pile[p][i]; pile[b].push_back(b);//把木塊b放回原位 } pile[p].resize(h+1); //pile只應保留下標0-h的元素 } //把第p堆高度為h及上方的木塊整體移動到p2堆得頂部 void pile_onto(int p, int h, int p2) { for(int i = h; i < pile[p].size(); i++) pile[p2].push_back(pile[p][i]); pile[p].resize(h); } void print() { for(int i = 0; i < n; i++) { printf("%d:", i); for(int j = 0; j < pile[i].size(); j++) printf("\n"); } } int main() { int a, b; cin >> n; string s1, s2; for(int i = 0; i < n; i++) pile[i].push_back(i); while(cin >> s1 >> a >> s2 >> b) { int pa, pb, ha, hb; find_block(a, pa, ha); find_block(b, pb, hb); if(pa == pb) continue; //非法指令,忽略 if(s2 == "onto") clear_above(pb, hb); if(s1 == "move") clear_above(pa, ha); pile_onto(pa, ha, pb); } print(); return 0; }
分析:上面的程式碼有一個值得學習的技巧:輸入一共4條指令,處理這種問題的更好的方法是 提取出指令鍵的共同點,編寫函式以減少重複程式碼。
clear()可以清空,resize()可以改變大小,push_back()和pop_back()分別是在尾部進行新增和刪除元素。empty()是測試是否為空。
執行效果:
c++中的集合set
問題:
/* 安迪的第一個字典 輸入一個文字,找出所有不同的單詞(連續的字母序列), 按字典序從小到大輸出,單詞不區分大小寫。 輸入: Adventures in Disneyland Two blondes were going to Disneyland when they came to a fork in the road.The sign read :"Disneyland Left."; So they go home. 輸出: a adventures blondes came disneyland */
程式碼:
#include<iostream> #include<algorithm> #include<string> #include<set> #include<sstream> using namespace std; set<string> dict; //string 字典集合 int main() { string s, buf; while(cin >> s) { for(int i = 0; i < s.length(); i++) if(isalpha(s[i])) s[i] = tolower(s[i]);else s[i] = ' '; stringstream ss(s); while(ss >> buf) dict.insert(buf); } for(set<string>::iterator it = dict.begin(); it != dict.end(); ++it) cout<< *it <<endl; return 0; }
分析:isalpha()判斷是否是一個字母,tolower函式轉化為統一的小寫
stringstream是輸入輸出流,進行型別轉化,insert()在集合中新增,
iterator是set物件中的迭代器,begin()和end()是指向開始和結束
*it取得當前物件的值
執行效果:
c++中的對映map:
問題:
/** 反片語 輸入一些單詞,找出所有滿足如下條件的單詞:該單詞不能通過字母重排,得到輸入的 文字中的另外一個單詞。在判斷是否滿足條件時,字母不區分大小寫,但在輸出是應 保留輸入中的大小寫,按字典序進行排序(所有大寫字母在小寫字母的前面) 輸入: ladder came tape soon leader acme RIDE lone Disk derail peat ScALE orb eye Rides dealer NotE LaCeS drIed noel dire mace Rob dries # 輸出: Disk Note derail drIed eye ladder soon */
程式碼:
#include<iostream> #include<string> #include<cctype> #include<vector> #include<map> #include<algorithm> using namespace std; map<string, int> cnt; vector<string> words; //將單詞s標準化 string repr(const string &s) { string ans = s; for (int i = 0; i < ans.length(); i++) { ans[i] = tolower(ans[i]); } sort(ans.begin(), ans.end()); return ans; } int main(){ int n = 0; string s; while (cin >> s) { if (s[0] == '#') break; words.push_back(s); string r = repr(s); //判斷cnt中是否存在鍵r if (!cnt.count(r)) cnt[r] = 0; cnt[r]++; } vector<string> ans; for (int i = 0; i < words.size(); i++) if (cnt[repr(words[i])] == 1) ans.push_back(words[i]); sort(words.begin(), words.end()); for (int i = 0; i < ans.size(); i++) cout << ans[i] << endl; return 0; }
分析:map有鍵和值,key value,同樣和set集合有類似的insert,find,count和remove操作,此外map還提供了[]運算子
insert是插入鍵值,find是查詢指定位置的值,count是查詢值是否存在,remove刪除。
執行效果:
c++中的棧
stack<int> s;
push()入棧,pop()出棧,top()取棧頂元素
c++中的佇列
queue<int> s; push入隊,pop出隊,front取隊首元素。
問題
/* 團體佇列 有t個團隊正在排一個長隊,每次新來一個人時,如果他有隊友在排隊, 那麼這個新人會插到最後一個隊友的身後。如果沒有任何一個隊友排隊 ,則他會排到長隊的隊尾。 輸入每個團隊中所有隊員的編號,要求支援如下3種指令(前兩種可以穿插進行) ENQUEUE x: 編號為x的人進入長隊 DEQUEUE :長隊的隊首出隊 STOP : 停止模擬 對於每個DEQUEUE 指令,輸出出隊的人的編號 */
程式碼:
#include<cstdio> #include<iostream> #include<queue> #include<map> using namespace std; const int maxt = 1000 + 10; int main() { int t, kase = 0; while (scanf("%d", &t) == 1 && t) { printf("Scenario #%d\n", ++kase); //記錄所有人的團隊編號 map<int, int> team; //team[x]表示編號為x的人所在的團隊編號 for (int i = 0; i < t; i++) { int n, x; scanf("%d", &n); while (n--) { scanf("%d", &x); team[x] = i; } } //模擬 queue<int> q, q2[maxt]; //q是團隊佇列,而q2[i]是團隊i成員的佇列 for (;;) { int x; char cmd[10]; scanf("%s", cmd); if (cmd[0] == 'S') break; else if (cmd[0] == 'D'){ int t = q.front(); printf("%d\n", q2[t].front()); q2[t].pop(); if (q2[t].empty()) q.pop();//團體t全體出佇列 } else if (cmd[0] == 'E') { scanf("%d", &x); int t = team[x]; if (q2[t].empty()) q.push(t);//團隊t進入佇列 q2[t].push(x); } } printf("\n"); } getchar(); getchar(); return 0; }
分析:
首先進行的是記錄所有大團隊的編號
用一個team對映鍵儲存每個隊的成員編號,值儲存每個隊的序號。team[編號] = 隊序號
然後是模擬階段,定義了一個q來儲存大團隊的佇列,q2儲存個成員所在佇列。
分析好了這個,下面就是各種命令就好辦了。
執行效果
c++中的優先佇列
問題
/* 醜數 醜數是指不能被2,3,5以外的其他素數整除的數。把醜數從小到大排列起來,結果如下 1,2,3,4,5,6,7,8,9,10,12,15,... 求第1500個醜數 */
程式碼
#include<iostream> #include<functional> #include<vector> #include<queue> #include<set> using namespace std; typedef long long LL; const int coeff[3] = {2, 3, 5};//係數 int main() { priority_queue < LL, vector<LL>, greater<LL> > pq; set<LL> s; pq.push(1); s.insert(1); for (int i = 1; ; i++) { LL x = pq.top(); pq.pop(); if (i == 1500) { cout << x << endl; break; } for (int j = 0; j < 3; j++) { LL x2 = x * coeff[j]; if (!s.count(x2)) { s.insert(x2); pq.push(x2); } } } getchar(); return 0; }
分析:
越小的整數優先順序越大的優先佇列可以用queue標頭檔案中的priority_queue<int, vector<int>, greater<int> > pq;
注意:greater比較模板在functional標頭檔案中,結尾的 > >不能寫在一起
先定義一個優先佇列pq來儲存所有已生成的醜數,集合s來儲存所有的醜數
然後取出隊首元素,這裡是取出一個數然後生成3個醜數,並判斷是否已經生成過。
答案:859963392
stl測試
先看程式碼:
#include<iostream> #include<cstdlib> #include<vector> #include<algorithm> #include<cassert> #include<ctime> using namespace std; void fill_random_int(vector<int>& v, int cnt) { v.clear(); for (int i = 0; i < cnt; i++) v.push_back(rand()); } void test_sort(vector<int>& v) { sort(v.begin(), v.end()); for (int i = 0; i < v.size() - 1; i++) assert(v[i] <= v[i+1]); } int main() { srand(time(NULL)); vector<int> v; fill_random_int(v, 1000000); test_sort(v); getchar(); return 0; }
分析:庫不一定沒有bug,我們要學會如何測試一個庫
首先需要用到隨機數,rand()函式實在cstdlib中,使用前需要srand(time(NULL))初始化隨機種子
我們定義一個函式fill_random_int來網vector可變陣列中存放隨機數,void clear(): 函式clear()刪除儲存在vector中的所有元素.
assert作用是:當表示式為真時無變化,但當表示式為假時強行終止程式
演算法題:
(1)Unix ls命令
問題
/* Unix ls命令問題 輸入正整數n以及n個檔名,排序後按列優先方式左對齊輸出。 假設最長檔名有M字元,則最右列有M字元,其他列都是M+2字元 */
程式碼
#include<iostream> #include<string> #include<algorithm> using namespace std; const int maxcol = 60; const int maxn = 100 + 5; string filename[maxn]; //輸出字串s,長度不足len時補字元extra void print(const string& s, int len, char extra) { cout << s; for (int i = 0; i < len - s.length(); i++) cout << extra; } int main() { int n; while (cin >> n) { int M = 0; for (int i = 0; i < n; i++) { cin >> filename[i]; M = max(M, (int)filename[i].length()); } //計算列數cols和行數rows int cols = (maxcol - M) / (M + 2) + 1, rows = (n - 1) / cols + 1; print("", 60, '-'); cout << endl; sort(filename, filename + n); for (int r = 0; r < rows; r++) { for (int c = 0; c < cols; c++) { int idx = c * rows + r; if (idx < n) print(filename[idx], c == cols - 1 ? M : M+2, ' '); } cout << "\n"; } } return 0; }
效果
相關文章
- 演算法競賽入門經典訓練指南 pdf演算法
- 演算法競賽入門經典--紫書6.3.1小球下落演算法
- 演算法競賽C++快速入門演算法C++
- kaggle再一次入門~經典入門級競賽~Titanic
- 劉汝佳《演算法競賽入門經典(第二版)》習題(三)演算法
- 《Flutter 入門經典》之“Flutter 入門 ”Flutter
- 經典加密演算法入門-RSA加密演算法
- Webpack經典入門Web
- ACM題解系列之一:劉汝佳:《演算法競賽入門經典》(第2版)ACM演算法
- 初學acmer--讀《演算法競賽入門經典》筆記(p36-41)ACM演算法筆記
- 《jQueryMobile入門經典》——2.5 問與答jQuery
- [轉]BI入門經典
- Kafka入門經典教程Kafka
- BI入門經典 (轉)
- 聊聊經典機器學習入門機器學習
- C語言入門經典(第5版)C語言
- 【Kaggle入門級競賽top5%排名經驗分享】— 建模篇
- 【Kaggle入門級競賽top5%排名經驗分享】— 分析篇
- Unix 入門經典 筆記筆記
- springboot入門經典Spring Boot
- 初學acmer--《演算法競賽經典入門》第二章2.5 習題自己的解答ACM演算法
- ACM演算法競賽_快速入門v0.1(施工中)ACM演算法
- 演算法競賽入門經典(第二版)第二章 習題2-2 韓信點兵演算法
- Python入門經典案例一Python
- 《jQueryMobile入門經典》——2.4 總結jQuery
- 最經典的黑客入門教程黑客
- 演算法競賽入門經典(第2版)-劉汝佳-第三章解題原始碼(C語言)演算法原始碼C語言
- 博弈論經典模型解析(入門級)模型
- 《HTML與CSS入門經典(第8版)》——1.7問與答HTMLCSS
- 《HTML與CSS入門經典(第8版)》——2.8作業HTMLCSS
- 《HTML與CSS入門經典(第8版)》——1.6總結HTMLCSS
- 資訊學競賽免費課程之C++語法入門網課影片C++
- 《HTML5+CSS3入門經典》——1.2 HTML5的優勢HTMLCSSS3
- Go語言入門經典第18章Go
- 嵌入式開發 ARM入門經典
- 新手入門經典:Jsp環境配置JS
- 大資料競賽平臺——Kaggle 入門篇大資料
- 《PHP、MySQL和Apache入門經典(第5版)》一2.9小結PHPMySqlApache