程式競賽
(slide ver)
You should read this slide along w/ this docs:
講師介紹
我不是專業的競程玩家,我沒有特別練習。
- YTP: 第二輪被幹掉 (高中)
- APCS: 4 3 (高中)
- CPE: 5/7
- PUPC: 2024 2025 銀獎
- ACM-ICPC: 某題首殺 (感謝看題隊友: 鮭魚)
- TOPC: 100/200名左右 (上週六打的)
目前戰績 (按照時間排序):
程式競賽介紹

需要學習的技術
程式能力: C++

請乖乖使用C++,不需要太高深的語法技巧
(甚至有些競賽跟你說只有部分題能用Python解)
(CPE前三題那種隨便,都是水題)
程式能力
高速寫程式碼,同時要兼顧可維護性。
英文能力
題目敘述都是英文
演算法
基礎: 一些資料結構得會的,尤其是樹。
Greedy: Activity Selection、Interval Cover、Optimal Matching、Huffman Coding、scheduling…
DP:
背包: 01背包、完全背包、分組背包、各種背包
subsets(bitmask)、LCS/LIS…
圖: D/BFS、MST(也算是greedy)、Shortest-Path(Dijkstra/Bellman/Floyd/SPFA…)、Topo/DAG、強連通分量(Tarjan)、Min Cut/Max Flow、LCA…
可能是技巧?: Disjoint Set、雙指標、滑窗、rolling hash、PQ、位元枚舉、分治、cache…
演算法
沒,亂講的
看需求,如果想要打得好就要碰一點
競賽制度
有分為 OI制 、 ICPC制
- 每一題有部份分,共200分
- 沒有罰時
- 看不到排行榜
- 每題只有對/錯
- 有罰時,一次20分鐘
- 可以邊打邊看榜
- 可以帶筆記/文件參賽(CPE不行)
ICPC:
OI (偏資奧):
賽制
不同題數: 題數高的優先 (廢話)
同題數: 看總時間,越少的優先 (也是廢話)

來看看這位玩家的分數吧
賽制
這傢伙不行啊

賽制
第1題比賽開始57分鐘後解出
第2題比賽開始20分鐘後解出
第3題比賽開始51分鐘後解出
第4題比賽開始175分鐘後解出,被罰時1次
第5題有提交2次,但沒解出來
第6題比賽開始134分鐘後解出
第7題沒有提交
57 + 20 + 51 + (175 + 20) + 134 = 457
分數: 5題,t: 457
競賽題目格式

提交作答結果
-
AC (Accepted)
-
WA (Wrong Answer)
-
RE (Runtime Error)
-
CE (Compile Error)
-
TLE (Time Limit Exceeded)
-
MLE (Memory Limit Exceeded)
程式正確
程式輸出有誤
程式跑到一半掛了
編譯錯誤
超時
記憶體用量超過限制
Online Judge
線上可以找到題目寫的地方
ZeroJudge
-
高中入門常用
-
中文
-
很多以前競賽的題目
-
有不少新手友善的題目
Online Judge
-
30年歷史,很多競賽題
-
很有名,很有名
-
題目量很大,幾萬題來的
TIOJ
-
屬於建中資訊社
-
不少有趣的校內賽題目可以打
台灣競賽/檢定資訊
APCS
大學程式設計先修檢測
分為 觀念/實作 兩部分
只想過畢業門檻的人不用看
CPE
大學程式能力檢定
7題
題源: onlinejudge.org
CPE
大學程式能力檢定
前3題,一定會有一題是從1星題題選(49題) 選出來的,把那49題都學會,你每次都可以保底1題。
ICPC
國際大學生程式設計競賽
程式設計競賽的最高殿堂
參賽資格難獲得
台灣最高規格的程式競賽就是ICPC亞洲區賽
ICPC
- 三人一組,
很考驗分工 - 有分洲區賽/Final
- 競賽結束前30mins會封榜,同時間開榜+頒獎很有看點。
ICPC
賽場都有吃的,賽後有晚餐,
比賽的時候,可以去休息室拿/吃零食
每隊可以帶一個吉祥物
玩偶/棉被 應該都行
ICPC
洲區賽參加資格:
-
NCPC (大專賽) 前40
- TOPC (線上賽) 前40
- PUPC (私立賽) 前10
- NCTU (科大賽) 前10
- CPE 前10隊(約30人?)
總之歡迎各位來跟我搶我們學校的參賽資格就是
比賽技巧
查榜大法

題目相關技巧
關於CPE前三題...
- 前三題都在送分
- 看到題目感覺有一點演算法的味道? 不可能! 直接暴力解!!!
- 同上,請你忘記進階演算法。
- 如果看不出解題思路,請自己加油。
- 如果有解題思路,但沒辦法變成程式,請自己加油。
看題技巧
可以先看看測資,
簡單題可能看得出一些端倪,甚至是直接看出題目要幹嘛。
難一點的,可能可以看得出是圖、動態規劃。
如果看到什麼要把結果 % 1000000007的
題目可能是:
- 動態規劃
- 排列組合
- 大數運算
- 圖
只想過畢業門檻的人不用看
如果看到什麼
- 從A~B有多少種方法
- 有多少個subset滿足條件
題目可能是:
- 動態規劃
- 推狀態轉移
只想過畢業門檻的人不用看
程式部分
bits/stdc++.h
有沒有人對於include標頭檔很頭痛的?
#include<iostream>
#include<cmath>
#include<algorithm>
#include<bitset>
#include<map>
#include<set>
#include<vector>
#include<iterator>
#include<deque>
#include<cstring>
#include<cctype>總之,有這麼一個東西可以用
#include
bits/
std
c++
.h
這還用說嗎?
這個檔案在 bits/ folder內
代表 standard (跟stdio/stdlib同意思)
代表 c++ (廢話)
標頭檔
#include<bits/stdc++.h>注意:
請不要在程式競賽以外的地方
像是你的專案,使用這個標頭。
using namespace std;
標準庫內很多東西,
函數、類別、變數都存在std命名空間內
std::cout << "Hello" << std::endl;
std::vector<int> v;引入std命名空間。
因此使用時無需使用std::
#include<bits/stdc++.h>
using namespace std;
int main() {
cout << "Hello" << endl; // 不用寫 std::cout 、 std::endl
vector<int> v; // 不用寫 std::vector
}宣告陣列的小技巧

像這一題,他說有N個人,1 <= N <= 30000
所以你可能會建立一個陣列: int arr[30000] = {};
但是你要存取1 ~ 30000,所以你會減1,像是: arr[id-1]
那你不如把陣列設成: int arr[30001] = {};
可以直接用 arr[id] 存取。
題目的輸入
char c = getchar(); // 讀一個字元,EOF -> -1
c = cin.get(); // 讀一個字元,EOF -> -1
cin >> c; // 讀一個非空白字元,EOF -> 0
cin.get(c); // 讀一個非空白字元,EOF -> -1讀字元
丟棄一個字元
cin.ignore();string s;
getline(cin, s);到第一個\n,讀成字串
從字串讀東西
string s;
// ...
stringstream ss(s);
int i, j, k;
ss >> i >> j >> k; // read as 3 integerswhile(cin >> i) {
}cin到EOF
string s;
while(getline(cin, s)) {
}char c;
while((c = getchar()) != EOF) {
}例題
UVA 382,2024/12/10 CPE 題2
L1: 多個N 到0為止
int N;
// 讀 N 到 EOF or N = 0
while (cin >> N && N) {
// handle your inputs
}
例題
UVA 11063,2025/05/20 CPE 題1
L1: 多個n (到EOF)
L2: n個數
int N;
while(cin >> N) { // 讀N到EOF
while(N--) { //讀N個b
int b;
cin >> b; // handle your inputs
}
}
UVA 10268,2024/12/10 CPE 題3
例題
L1: 多個n (到EOF)
L2: 一行數
long long x;
while (cin >> x) {
cin.ignore();
string line;
getline(cin, line);
stringstream ss(line);
long long c;
while (ss >> c) {
// handle your inputs
}
}例題
UVA 941,2023/10/17 CPE 題6
L1: 給T,T個Sample
L2: 一行字 + 一數
int T;
cin >> T;
int N;
string s;
while (T--) { // run T times
cin >> s >> N;
// handle your inputs
}I/O Optmization
'\n' vs endl
'\n' vs endl
'\n' 就是一個換行字元
endl 是一個操作符,換行 + flush buffer
flush 是 flush buffer 用的操作符
或是呼叫 cout.flush() 也行
'\n' vs endl
所以,如果有需要大量換行,可以使用 '\n'
最後在 cout << flush;
或是 cout.flush();
不要一直使用 cout << endl;
#include <bits/stdc++.h>
using namespace std;
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
}C/C++ I/O 解除綁定
(其實這個我也用不到)
IO優化基礎兩件套
ios::sync_with_stdio(false);取消C++與C的 I/O綁定
(預設每次C++ I/O 時,會去檢查C的I/O Buffer)
ios::sync_with_stdio(false);影響:
- 不能同時使用cin / scanf
- 不能同時使用cout / printf
cin.tie(nullptr);取消cin與cout的綁定
(預設每次cin read前,都會flush一次cout)
cin.tie(&cout);cin.tie(&cout);只想過畢業門檻的人不用看
char unlocked
(這個屬於黑科技)
把 getchar() 改成 getchar_unlocked()
把 putchar() 改成 putchar_unlocked()
總之,這個東西沒有Thread Safety,稍快。
STL Container
Standard Template Library 內有這麼一些Container
用於存放/管理元素。
<Template> 可以存放任意型別的元素。
已經抽象過底層的資料結構,提供統一操作介面。
Iterator
每個容器皆有Iterator可以使用,
可以想像是: 指向容器元素的指標
但比指標更抽象
假設今天它是vector<int>的迭代器, 型別就是vector<int>::iterator 但我們可以用auto代替
通常,會有這四種取得迭代器的基本方法
(有些沒有,如stack / queue)

大多可以用 (+、-) (加、減) 來移動

a.begin() 指向第一個元素 *a.begin() 是第一個元素
a.begin()+1 指向第二個元素 *(a.begin()+1) 是第二個元素
但是其實我們不常使用Iterator。
我們只要先記:
- Iterator是元素的指標
-
*a.begin()取第一個元素 -
*a.rbegin()取最後一個元素
string
宣告
string s1 = "Hello"; // "Hello"
string s2("world"); // "world"
string s3(5, "!"); // "!!!!!"字元讀寫
string s = "Hello"; // "Hello"
char c = s[2]; // 'l'
s[1] = "a";
cout << s; // "Hallo"字串長度
s.length()
s.size()concatenation
string s1 = "Hello";
string s2 = "World";
string s3 = s1 + " " + s2; // "Hello World"substring
string s = "Hello World";
// s.substr(offset, length)
// 從s[0]取五個字元
string s2 = s.substr(0, 5); // "Hello"
// s.substr(offset)
// 從s[6]取到尾
string s3 = s.substr(6); // "World"
cout << s; // "Hello World"不改變原有的string內容
insert / erase / replace
string s = "Hello World";
// s.insert(offset, str)
// 在 s[offset] 前面插入
s.insert(5, ","); // "Hello, World"
// s.erase(offset, length)
// 從 s[offset] 開始刪除 length個字元
s.erase(5, 1); // "Hello World"
// s.replace(offset, length, newString)
// 從s[offset] 開始刪除 length 個字元,換成newString
s.replace(6, 5, "World!!!!!"); // "Hello World!!!!!"
cout << s; // "Hello World!!!!!"改變原有的string內容
insert / erase / replace
string s = "Hello World";
// "Hello, World"
string s2 = s.substr(0, 5) + "," + s.substr(5);
// "HelloWorld"
string s3 = s.substr(0, 5) + s.substr(5 + 1);
// "Hello World!!!!!"
string s4 = s = s.substr(0, 6) + "World!!!!!" + s.substr(6 + 5); 不改變原有的string內容
find
string s = "I like C++ and C++ is powerful";
// find where "C++" is
size_t pos = s.find("C++");
// 如果不是npos (一個常數,代表沒找著)
if (pos != string::npos) {
cout << "第一次出現於: " << pos << "\n";
}push_back / pop_back
string s = "123456";
s.push_back('7'); // "1234567"
s.pop_back(); // "123456"
s.pop_back(); // "12345"
cout << s; // "12345"改變原有的string內容
number conversion
string s = "123";
int x = stoi(s); // string to int
double y = stod(s); // string to double
int i = 456;
string s = to_string(i); // int to string
cout << s; // 456iteration
for (int i = 0; i < s.length(); i++) {
cout << s[i];
}
for (char c : s) {
cout << c;
}vector
- 動態陣列
- O(1) 隨機存取
- O(1) 插刪尾
- 底層: array
vector
宣告
vector<int> v; // {}
vector<int> v(5); // {0, 0, 0, 0, 0}
vector<int> v(3, 1); // {1, 1, 1}
vector<int> v = {1, 2, 3};多層vector
vector<vector<int>> v2; // {}
vector<vector<int>> v2(5); // {{}, {}, {}, {}, {}}想要多層vector,就是vector內有vector嘛
多層vector
// {{0, 0}, {0, 0}, {0, 0}}
vector<vector<int>> v2(3, vector<int>(2));
// {{4, 4}, {4, 4}, {4, 4}}
vector<vector<int>> v2(3, vector<int>(2, 4));
建構子第二個參數表示內層
操作
// 插尾
v.push_back(val) // O(1), O(n) on realloc
v.emplace_back(args)
// 在it插入val,回傳val的it
v.insert(it, val) // O(n) worst
v.emplace(it, args)
// 在it插入n個val,回傳第一個val的it
v.insert(it, n, val); // O(n + n(move))
// 在it插入[itfirst, itend),回傳第一個val的it
v.insert(it, itbegin, itend); // O(n + m)增
操作
// 刪尾
v.pop_back() // O(1)
// 刪除it的元素,回傳下一個有效iterator
v.erase(it) // O(n) worst
// 刪除[itfirst, itend),回傳下一個有效it
s.erase(itfirst, itend) // O(n)刪
操作
// 讀取第i個
int x = v[i] // O(1)
// 安全讀取第i個
int i2 = v.at(i) // O(1)
// 讀頭
int front = v.front() // O(1)
// 讀尾
int back = v.back() // O(1)讀
操作
// 查看是否為空
bool isEmpty = v.empty() // O(1)
// 清空
v.clear() // O(n)
// 查看大小
int size = v.size() // O(1)三個基本方法
vector<int> v = {2,1,3};
v.push_back(4);
v.push_back(5);
v.pop_back();
v[1] = 6;2
1
3
2
1
3
2
1
3
2
1
3
4
4
4
5
4
2
6
3
4
vector<int> v = {2,1,3};
v.push_back(4);
v.push_back(5);
v.pop_back();
v[1] = 6;2
1
3
2
1
3
2
1
3
2
1
3
4
4
4
5
4
2
6
3
4
*v.begin()

iteration
for (int i = 0; i < v.size(); i++) {
cout << v[i];
}
for (int i : v) {
cout << i;
}
// 這裡 auto 應該是 vector<int>
for (auto v : v2) {
for (int i : v) {
cout << i;
}
} vector<int> v = {1,2,3,4,5};
auto it = v.begin(); // 指向第1個元素
int i = *it; // 第1個元素
cout << i << "\n"; // 1
auto it2 = v.begin() + 1; // 指向第2個元素
int i2 = *it2; // 第2個元素
cout << i2 << "\n"; // 2
auto it3 = v.begin() + 2; // 指向第3個元素
*it3 = 100; //直接更改iterator指向的元素
cout << *it3; // 100
cout << v[2]; // 100接續前一篇的iterator: 例子
vector<int> v = {1,2,3,4,5};
for (auto it = v.begin(); it != v.end(); ++it) {
cout << *it << " "; // 1 2 3 4 5
}
for (auto rit = v.rbegin(); rit != v.rend(); ++rit) {
cout << *rit << " "; // 5 4 3 2 1
}接續前一篇的iterator: 走訪vector
例題
翻譯:
每個T給10個網站 + 分數
輸出最高分數的網站
#include <bits/stdc++.h>
using namespace std;
int main() {
int T,score;
cin >> T;
string s;
vector<string> c;
for (int t = 1; t <= T;t++) {
cout << "Case #" << t << ":\n";
int max = -1;
for (int i = 0; i < 10; i++) {
cin >> s >> score;
if (score >= max) {
if (score > max) {
c.clear();
}
max = score;
c.push_back(s);
}
}
for(string s : c) {
cout << s <<"\n";
}
}
}deque
deque
- 雙端佇列
- 接近O(1) 隨機存取
- O(1) 插刪頭尾
- 底層: segment array
宣告
deque<int> dq; // {}
deque<int> dq(5); // {0, 0, 0, 0, 0}
deque<int> dq(3, 1); // {1, 1, 1}
deque<int> dq = {1, 2, 3};多層dq
deque<deque<int>> dq2; // {}
deque<deque<int>> dq2(5); // {{}, {}, {}, {}, {}}想要多層dq,就是dq內有dq嘛
多層dq
// {{0, 0}, {0, 0}, {0, 0}}
deque<deque<int>> dq2(3, deque<int>(2));
// {{4, 4}, {4, 4}, {4, 4}}
deque<deque<int>> dq2(3, deque<int>(2, 4));
(與vector相同)
建構子第二個參數表示內層
操作
// 插尾
dq.push_back(val); // O(1) amortized
dq.emplace_back(args);
// 插頭
dq.push_front(val); // O(1) amortized
dq.emplace_front(args);
// 在it插入val,回傳val的it
dq.insert(it, val) // O(n) worst
dq.emplace(it, args)
// 在it插入n個val,回傳第一個val的it
dq.insert(it, n, val); // O(n + n(move))
// 在it插入[itfirst, itend),回傳第一個val的it
dq.insert(it, itbegin, itend); // O(n + m)增 (比vector多了插頭)
操作
// 刪尾
dq.pop_back(); // O(1)
// 刪頭
dq.pop_front(); // O(1)
// 刪除it的元素,回傳下一個有效it
dq.erase(it) // O(n) worst
// 刪除[itfirst, itend),回傳下一個有效it
dq.erase(itfirst, itend) // O(n)刪
操作
// 讀取第i個
int i = dq[i] // 極接近O(1)
// 安全讀取第i個
int i2 = dq.at(i) // 極接近O(1)
// 讀頭
int front = dq.front() // O(1)
// 讀尾
int back = dq.back() // O(1)讀
(與vector相同)
操作
三個基本方法
// 查看是否為空
bool isEmpty = dq.empty() // O(1)
// 清空
dq.clear() // O(n)
// 查看大小
int size = dq.size() // O(1)deque<int> dq = {2,1,3};
dq.push_back(4);
dq.push_front(5);
dq.pop_back();
dq[1] = 6;2
1
3
2
1
3
2
1
3
5
2
1
4
4
4
5
3
5
6
1
3
deque<int> dq = {2,1,3};
dq.push_back(4);
dq.push_front(5);
dq.pop_back();
dq[1] = 6;2
1
3
2
1
3
2
1
3
5
2
1
4
4
4
5
3
5
6
1
3
*dq.begin()
stack & queue
Stack:
- 先入後出
- 括號/字串處理
- DFS
- 遞迴 (模擬call stack)
- Backtracking

資料結構應該都學過
Queue:
- 先入先出
- unweighted BFS/pathfinding
- Sliding Window
這兩個在STL中,底層都是以deque實現
Stack
push_back() / pop_back()
back()
宣告
stack<int> s; 操作
// push val
s.push(val); // O(1)
s.emplace(args);
// pop
s.pop(); // O(1)增刪
也就是deque的push_back(val)與pop_back()
操作
// 讀取頂部元素
int i = s.top();讀
也就是deque的back()
操作
// 查看是否為空
bool isEmpty = s.empty() // O(1)
// 查看大小
int size = s.size() // O(1)兩個基本方法
操作
while (!s.empty()) {
s.pop();
}清空
s = stack<int>();重新建立空的stack
stack<int> s;
s.push(5);
s.push(4);
s.push(3);
s.pop();
s.push(7);5
4
5
4
3
5
5
4
s.top()
5
4
7
5
4
3
4
7
stack<int> s;
s.push(5);
s.push(4);
s.push(3);
s.pop();
s.push(7);5
4
5
4
3
5
5
4
s.top()
5
4
7
5
4
3
4
7
例題
翻譯:
T行
Sleep: 入某人的夢
Kick: 退出一層
Test: 在誰的夢裡
#include <bits/stdc++.h>
using namespace std;
int main() {
stack<string> dreams;
int T;
cin >> T;
while (T--) {
string s;
cin >> s;
if (s == "Sleep") {
string name;
cin >> name;
dreams.push(name);
} else if(s == "Kick") {
if(!dreams.empty())
dreams.pop();
} else if(s == "Test") {
if(dreams.empty())
cout << "Not in a dream\n";
else
cout << dreams.top() <<"\n";
}
}
}Queue
push_back() / pop_front()
back() / front()
宣告
queue<int> q; 操作
// push val
q.push(val); / q.emplace(val) // O(1)
// pop
q.pop(); // O(1)增刪
也就是deque的push_back(val)與pop_front()
操作
// 讀取 front
int front = s.front();
// 讀取 back
int back = s.back();讀
與deque相同
操作
// 查看是否為空
bool isEmpty = q.empty() // O(1)
// 查看大小
int size = q.size() // O(1)兩個基本方法
操作
while (!q.empty()) {
q.pop();
}清空
q = stack<int>();重新建立空的stack
queue<int> s;
s.push(5);
s.push(4);
s.push(3);
s.pop();
s.push(7);5
4
5
4
3
5
4
3
4
3
7
5
5
5
4
4
5
4
3
3
7
s.front()
s.back()
queue<int> s;
s.push(5);
s.push(4);
s.push(3);
s.pop();
s.push(7);5
4
5
4
3
5
4
3
4
3
7
5
5
5
4
4
5
4
3
3
7
s.front()
s.back()
例題
抱歉,沒有找到簡單的題目。
- UVA 439 (DFS)
- UVA 532 (3D DFS)
- UVA 928 (DFS)
- UVA 122 (拆樹)
set
- 就是集合。
- 不允許重複元素
- 自動排序: 按照值的 < 排序
- 無法random access
set
宣告
set<int> s;#include <bits/stdc++.h>
using namespace std;
struct CompareMethod {
bool operator()(const int &a, const int &b) const {
return a > b; // 改成大到小
}
};
int main() {
set<int, CompareMethod> s = {1, 5, 3, 2, 4};
for (int x : s)
cout << x << " "; // 5 4 3 2 1
}排序方法預設是按照物件類型預設的 < 排序法,
像是 int 的 < 是 "小在前,大在後"
你可以使用自訂的排序法
操作
// 插入val,回傳pair<iterator, bool>(val的it, 是否是新元素)
s.insert(val) // O(log n)
s.emplace(args)
// 插入[itfirst, itend)
s.insert(itbegin, itend); // O(n + m)增
操作
// 刪除元素,回傳刪除的個數 (0 or 1)
s.erase(val) // O(log n)
// 刪除it的元素,回傳下一個有效it
s.erase(it) // O(log n)
// 刪除[first it, end it),回傳下一個有效it
s.erase(itfirst, itend) // O(log n)刪
操作
// 回傳指向key的it,沒有的話回傳s.end()
s.find(key) // O(log n)
// 回傳key個數 (0 or 1)
s.count(key) // O(log n)
// 回傳指向 "第一個>= key的值" it
s.lower_bound(key) // O(log n)
// 回傳指向 "第一個> key的值" it
s.upper_bound(key) // O(log n)找
操作
三個基本方法
// 查看是否為空
bool isEmpty = s.empty() // O(1)
// 清空
s.clear() // O(n)
// 查看大小
int size = s.size() // O(1)set<int> s;
s.insert(6);
s.insert(4);
s.insert(5);
s.erase(4);
s.push(7);6
6
4
5
6
4
5
6
5
6
7
6
4
4
5
5
*s.begin()
iteration
for (int i : s) {
cout << i;
}例題
翻譯:
給你一個文章
要你把出現過的單字,字典序,小寫輸出
看到字典序應該就想到能用set放字串了吧?
#include<bits/stdc++.h>
using namespace std;
int main() {
set<string> words;
string s;
char c;
while ((c = getchar()) != EOF) {
if (isalpha(c)) {
s += tolower(c);
} else {
words.insert(s);
s = "";
}
}
words.erase("");
for(string w : words) {
cout << w << "\n";
}
}pair
能存儲兩個不同類型的值
成員: first, second
以first < 再來 second < 排序
pair
今天假設你有個Student,有id跟name:
struct Student {
int id;
string name;
};Student s = {31, "蔡詠竣"};我,身為一個懶人,偶爾想用pair<int, string> 代替。
pair<int, string> me = {31, "蔡詠竣"};宣告
pair<int, string> p;
p.first = 31; // 更改first
p.second = "蔡詠竣"; // 更改second
pair<int, string> p2 = make_pair(31, "蔡詠竣");
pair<int, string> p3(31, "蔡詠竣");
pair<int, string> p4 = {31, "蔡詠竣"};vector<pair<int, string>> students;
students.push_back({31, "蔡詠竣"});
pair<int, string> me = students[0];
cout << me.first << " " << me.second;一樣可以放於vector內,偶爾蠻好用的。
像這樣就是一個可以random access的k-v pair。
不用寫兩個vector對著。
pair<int, string> p1 = {31, "蔡詠竣"},
p2 = {30, "張乃文"},
p3 = {42, "黃芋泥"};
vector<pair<int, string>> v{p1, p2, p3};
for (auto i : v) { // 把 pair<int, string> 用 auto 代替
cout << i.first << " " << i.second << "\n";
}
cout << "\n";
sort(v.begin(), v.end()); // 排序
for (auto i : v) {
cout << i.first << " " << i.second << "\n";
}}前面說過了,使用first < 排序
31 蔡詠竣
30 張乃文
42 黃芋泥
30 張乃文
31 蔡詠竣
42 黃芋泥tuple
能存儲>=3個不同類型的值
成員存取方式稍有不同
從第一個元素排,再來第二個,...
tuple
簡單講就是超級懶/臨時想用 可以用。
使用方法
tuple<int, int, int> coord = {1, 2, 3};
int y = get<1>(coord); // 2
get<2>(coord) = 4; // coord 變成 {1, 2, 4}
vector<tuple<int,int,int>> v;
v.push_back(coord);
v.emplace_back(10, 20, 30);
for (auto &[x,y,z] : v) { // C++17 綁定大法
cout << x << " " << y << " " << z << "\n";
}map
- 每一個元素都是(key, value)形式,也就是pair。
- key不能重複
- 自動排序: 按照 key 的 < 排序
- 底層: RB-tree
map
可以想像成是set<pair<K,V>>
或是不排序的vector<V> (K是數字)
宣告
map<int, string> m;
map<int, string> m2 = {{1,"one"}, {2,"two"}};操作
// 如果k不存在,插入{k, v},如果存在,更新v
m[k] = v; // O(log n)
// 插入p,回傳pair<iterator, bool>(p的it, 是否是新元素)
m.insert(pair<K, T> p); // O(log n)
m.emplace(k, v)
// 插入[itfirst, itend)
m.insert(itbegin, itend) // O(m log(n+m))增
操作
// 刪除key,回傳刪除的個數 (0 or 1)
m.erase(key) // O(log n)
// 刪除it的元素,回傳下一個有效it
m.erase(it) // O(log n)
// 刪除[first it, end it),回傳下一個有效it
m.erase(itfirst, itend) // O(k log n)刪
操作
// 回傳指向key的it,沒有的話回傳m.end()
m.find(key) // O(log n)
// 回傳key個數 (0 or 1)
m.count(key) // O(log n)
// 回傳指向 "第一個>= key的值" it
m.lower_bound(key) // O(log n)
// 回傳指向 "第一個> key的值" it
m.upper_bound(key) // O(log n)找
操作
// 存取key為k的value
// 如果不存在,會插入{k, (default value)},並回傳value。
m[k]; // O(log n)讀
操作
三個基本方法
// 查看是否為空
bool isEmpty = m.empty() // O(1)
// 清空
m.clear() // O(n)
// 查看大小
int size = m.size() // O(1)map<int, string> m;
m[2] = "b";
m[1] = "a";
m[3] = "c";
m.erase(2);{2,"b"}
{1,"a"}
{1,"a"}
{2,"b"}
{3,"c"}
{3,"c"}
{2,"b"}
{1,"a"}
什麼時候用map ? 什麼時候用vec/dq ?
map: 需要排序、key不知道範圍、key不是數字
vec/dq: 不用排序(排序也行),key有範圍
例題
翻譯:
給你 英文 + 外文
再來,給 外文 要你找 英文
#include <bits/stdc++.h>
using namespace std;
int main() {
_____________________ dict;
string line, eng, foreign;
while (getline(cin, line) && line != "") {
stringstream ss(line); // 拆分 line 成兩個字串
ss >> eng >> foreign;
_____________________;
}
while (cin >> foreign) {
if (_____________________)
cout << _____________________ << "\n";
else
cout << "eh\n";
}
}通用方法
find(v.begin(), v.end(), val);查找val的it
Find
查找val的出現次數
count(v.begin(), v.end(), val);Count
把元素reverse
reverse(v.begin(), v.end());Reverse
vector<int> v = {1, 2, 3, 4, 5};
// reverse vector
reverse(v.begin(), v.end());
for (int x : v) // 5 4 3 2 1
cout << x << " ";Reverse
回傳bool, 看val在不在
binary_search(v.begin(), v.end(), val);binary_search
排序
sort(v.begin(), v.end());Sort
sort(v.begin(), v.end(), [](int a, int b)) {
/*
a在b前 -> return true,反之false
*/
return a > b;
}自訂排序 (int為例)
例題
UVA 10268,2024/10/15 CPE 題2
翻譯:
每加一個數,找一次中位數
#include <bits/stdc++.h>
using namespace std;
int main() {
int i;
vector<int> v;
while(cin >> i) {
v.push_back(i);
sort(v.begin(), v.end());
if(v.size() % 2 == 1) {
cout << v[v.size()/2] << "\n";
} else {
cout << (v[v.size()/2] + v[v.size()/2-1])/2 << "\n";
}
}
}一些utilties
bool b = isalpha(c);
bool b = isdigit(c);
c = toupper(c);
c = tolower(c);字元處理
int gcd = __gcd(a, b);
int lcm = lcm(a, b);gcd & lcm
string s = "10110101";
bitset<8>(s).to_ulong();number to bits string
bits string to ulong
bitset也是STL容器之一,我覺得還不用細講。
int i = 12345;
bitset<32>(i).to_string();質數表
從我大一到大三,這題出現過兩次了
其實是反質數表
int size = 1000001;
bool notprime[1000001] = {};
notprime[0] = true;
notprime[1] = true;
int lim = sqrt(size);
for (int i = 2; i <= lim; i++) {
if (!notprime[i]) {
int k = i * i;
while(k < size) {
notprime[k] = true;
k += i;
}
}
}
// 看100是不是質數
bool isPrime = !notprime[100];
例題
2025/05/20 CPE 題4
翻譯:
給多個n (T)
找n = a + b,a <= b,a和b都是質數
題解過長
最後
謝謝各位。
程式競賽
By speedcubing
程式競賽
- 122