建國中學 游承曦
C++ Standard Template Library
Animation from FHVirus Orz
push(x) 將元素x放到Stack頂端
top() 查詢Stack頂端元素
pop() 將Stack頂端元素取出
【 Stack 操作都是先進後出 】
const int N = 1e5;
struct Stack{
int stack[N];
int top = 0;
void push(int x){
if(top == N){
cout << "Full!\n";
return;
}
else{
stack[top] = x;
top++;
}
}
int pop(){
if(top == 0){
cout << "Empty!\n";
return -1;
}
else{
top--;
return stack[top];
}
}
} stk;
#include <stack>
int main()
{
stack<int> st; // 宣告
st.push(1);
st.push(2);
cout << st.top(); // 2
st.pop();
cout << st.top(); // 1
st.pop();
cout << st.size(); // 0
cout << st.empty(); // 1
}
裸 Stack
一開始有個車站如下圖,在右側有車廂編號\(1\) ~ \(n\) 依序進站。現在,我們想將火車重新編號為 \(a_1, a_2, ..., a_n\)出站,但先進站的車廂只能等後進站的車廂出站才能離開。給定\(n\)與\(a_1, a_2, ... a_n\),試問有沒有辦法達成此排列方式?
給你一堆()[]<>{},判斷是否配對成功
成功例子: {()<([()])>}
失敗例子: ((){[>}{)
給定一排牛 (不是一牛排) 及每頭牛的高度,
牠們只能往右平視或俯視,
請問牠們分別能夠看到幾頭牛?
(如果兩頭牛的高度一樣,
那麼左邊的牛的視野只能看到右邊的牛為止)
\(N \leq 10^6\) ,顯然\(O(n^2)\)會爆
Hint : 維護單調性 (monotone)
(經典題) 給\(N\)個寬度為\(1\)的長條圖,
求其所形成的最大矩形面積
:跟上面那題很像,而且它還可以延伸
KYW
不能從中間插隊
不能從中間出來
可以從前面離開
可以從後面加入排隊
push(x) 將元素x放到Queue尾端
top() 查詢Queue前端元素
pop() 將Queue前端元素取出
【 Queue 操作都是先進先出 】
const int N = 1e5;
struct Queue{
int queue[N];
int front = 0, back = 0;
void push(int x){
if(back == N){
cout << "Error!\n";
return;
}
else{
queue[back] = x;
back++;
}
}
int pop(){
if(front == back){
cout << "Empty!\n";
return -1;
}
else{
int ret = queue[front];
front++;
return ret;
}
}
} qu;
但這種實作方式有空間有限性,
所以有另一種延伸的實作叫Cycling Queue
#include <queue>
int main()
{
queue<int> qu; // 宣告
qu.push(1);
qu.push(2);
cout << qu.front(); // 1
qu.pop();
cout << qu.front(); // 2
cout << qu.size(); // 1
cout << qu.empty(); // 0
}
裸 Queue
現在手上有\(N\)張牌,由上而下編號\(1\)~\(N\)。當牌數量剩餘兩張以上時,丟掉最上面的一張,再把當前最上面的排放到底部。模擬輸出丟牌的過程?
(挑戰) 有一堆人要去排隊,每個人可能是\(M\)個組中其中一組的成員。當一個人要進去排隊時,若有同組的已經在隊伍裡,則可以插隊到自己組的尾端,否則就排到隊伍尾端。給\(M\)個組的組員和一堆enqueue / dequeue指令,模擬排隊情況。
#include <deque>
int main()
{
deque<int> dq; // 宣告
dq.push_back(1); // 1
dq.push_back(2); // 1 2
dq.push_front(3); // 3 1 2
dq.push_front(4); // 4 3 1 2
cout << dq.front(); // 4
cout << dq.back(); // 2
// pop_front()
// pop_back()
// size()
// empty()
}
給一個序列 \(A\) 和一個數 \(K\),
問你這個序列每 \(K\) 個數的最大值和最小值為何?
\(1 \leq K \leq N \leq 10^6\)
\(A: [5, 1, 3, 2, 4], \ K=3\)
ZJ a146 - Sliding Window 但我RE,肝
我甚至不知道為什麼
給你 \(N\) 個士兵的戰力值 \(x_i\) ,要把他們分成一些連續的組別,一個組別的總戰力值計算方式為
\(x' = a(\sum{x_i})^2 + b\sum{x_i} + c\),
你希望所有組別的戰力和為最大,求總戰力和?
\(N \leq 10^6\)
我只是放爽的
#include <vector>
int main()
{
vector<int> v; // 宣告
v.push_back(1); // 把1從後面加進去
v.push_back(2); // 1 2
v.push_back(3); // 1 2 3
v.pop_back(); // 1 2
cout << v[1]; // 2
// 跟陣列一樣
cout << v.size(); // 2
// 有幾個元素
cout << v.empty(); // 0
cout << v.front() << ' ' << v.back() << '\n'; // 1 2
}
#include <vector>
int main()
{
vector<int> v(10, 100);
v.emplace_back(111);
cout << v[9] << '\n'; // 100
cout << v[10] << '\n'; // 111
v.resize(5);
v.clear();
}
v.begin() 、 v.end()
其實就是指標拉(X
v.begin() 是指向第一個元素的位置
v.end() 指向最後一個元素 "的下一個位置"
(之後可能會用到)
講師很偷餒
#include <string>
int main()
{
string x = "aba", y = "abc";
string z = x + y;
cout << z << endl; // abaabc
x += y;
cout << x << endl; // abaabc
cout << (x < y) << endl; // 1
}
叩
#include <utility>
int main()
{
pair<int, int> a;
pair<int, double> b;
pair<string, int> c;
a.first = 1, a.second = 2;
b.first = 3, b.second = 4.321;
c.first = "YAAAA", c.second = 6;
pair<int, int> d = {7, 8};
}
每一個值都是一棵二元樹的節點,
對於一個節點,他的值都會比所有子節點大(or小)
根節點是最大值(or最小值)
:什麼是二元樹辣齁
Heap是一棵平衡二元樹,深度最深與深度最淺相
差不超過1層。儲存二元樹可以用一個陣列來存,
如果是1-base,對於一個節點\(o\),
左子節點為\(2o\),右子節點為\(2o+1\)
查詢最大值 - 查詢根節點
插入一個值val
刪除最大值 - 刪除根節點
const int N = 1e5;
int heap[N], sz = 0;
int getMX(){
return heap[1];
}
void push(int val){
heap[++sz] = val;
int idx = sz;
while(heap[idx] > heap[idx/2] && idx/2 > 0)
swap(heap[idx], heap[idx/2]), idx /= 2;
}
void pop(){
swap(heap[1], heap[sz]), sz--;
int idx = 1;
while(heap[idx] > max(heap[idx*2], heap[idx*2+1]) && idx*2 <= sz){
if(idx*2 == sz){ // 防止超出邊界
if(heap[idx] > heap[idx*2])
swap(heap[idx], heap[idx*2]);
break;
}
if(heap[idx*2] > heap[idx*2+1])
swap(heap[idx], heap[idx*2]), idx *= 2;
else
swap(heap[idx], heap[idx*2+1]), idx = idx*2+1;
}
}
void pop(){
swap(heap[1], heap[sz]), sz--;
int idx = 1;
while(heap[idx] > max(heap[idx*2], heap[idx*2+1]) && idx*2 <= sz){
if(idx*2 == sz){
if(heap[idx] > heap[idx*2])
swap(heap[idx], heap[idx*2]);
break;
}
if(heap[idx*2] > heap[idx*2+1])
swap(heap[idx], heap[idx*2]), idx *= 2;
else
swap(heap[idx], heap[idx*2+1]), idx = idx*2+1;
}
}
#include <queue>
int main()
{
priority_queue<int> pq; // 宣告
pq.push(2);
pq.push(1);
cout << pq.top(); // 2
pq.push(3);
cout << pq.top(); // 3
pq.pop();
cout << pq.top(); // 2
// pq.size()
// pq.empty()
}
Max Heap Property
by priority_queue
#include <queue>
#include <vector>
int main()
{
priority_queue<int, vector<int>, greater<int>> pq; // 宣告
pq.push(2);
pq.push(1);
cout << pq.top(); // 1
pq.push(3);
cout << pq.top(); // 1
pq.pop();
cout << pq.top(); // 2
// pq.size()
// pq.empty()
}
Min Heap Property
其實不只最大最小值的Heap,
還可以自定義狀態,
之後碰到題目再說
(經典題) 給\(N\)個數字\(a_1, a_2, ..., a_N\),需要把數字兩兩相加直到剩下一個數字,但相加會有一個cost,cost計算方式為兩數之和,求合併所有數字的最小cost總和
有一天,你經過了一家飲料店,發現有\(N\)個人排隊要買飲料。不過,這家飲料店人手充足,一共有\(M\)個店員可以服務這些排隊的人潮。
為了公平起見,雖然店員很多,但是排隊只排成一列,避免在排很多列的情況下,每列前進的速度會不一樣。另外,為了服務品質起見,這家店的店員必須遵守兩個規則:必須按照客人排隊順序服務客人,不能先服務排在後面的顧客,否則前面的客人可能會森77(而這是飲料店最不想看到的狀況);另外,如果某個店員服務了某個客人,則該客人點的所有飲料都要由該店員製作,製作完成之後才能服務下一位客人。
你做了一下市場調查,詢問每位排隊的客人要買幾杯飲料。假如所有的店員製作飲料的速度都是每分鐘1杯,在遵守這些規則的前提下,請問服務完這些客人至少需要多久?
\(N \leq 10^6, M \leq 10^4, a_n \leq 1000\)
或者就是數學上的集合辣
簡單來說是維護一棵自動平衡二元樹
(通常是什麼紅黑樹、AVL Tree...),
每次二分搜看有沒有找到,實作非常複雜,
code通常都幾百行(?),有興趣自己去查>3
Treap or SplayTree or LinkCutTree
#include <set>
int main()
{
set<int> st; // 宣告
st.insert(3);
cout << st.count(3); // 1
cout << st.size(); // 1
cout << st.count(5); // 0
st.erase(5);
}
一般的Set同樣元素只能出現一個,
所以count只會是0或1
你獲得了一張紙條,上面有\(N\)個數字,你要照著上面的數字開關燈泡,一開始所有燈泡都是關的,當到了第\(i\)個數字,需要操作第\(a_i\)個開關(開變關,關變開),已知最後只會剩下一個燈泡是開著的,求那個燈泡編號
\(N \leq 10^5, a_i \leq 2^{31}\)
Hint : 其實有\(O(N)\)解,用位元運算
ZJ d708 - 小王的积木 也是同樣的想法
維護一個集合,支援插入數字、
查詢最大最小值和刪除最大最小值
Hint : multiset
有一個索引值 和 他的值
#include <map>
int main()
{
map<int, int> mp; // 宣告
// {key, value}
mp.insert(2);
cout << mp[2]; // 1
mp[2]++;
cout << mp[2]; // 2
cout << mp[1]; // 0
}
map的用法很多元,大家可以自己去查XD
你獲得了一個工作!計算森林中各樹種的比例。有\(N\)棵樹,給樹種的英文名字,依照字典序輸出他們佔的比例
(比例計算方式:\( \frac{樹種出現的次數}{N} \))
其實我不知道我做這份slides的時候為什麼要放鯊鯊可能壓力太大,記得多多關懷講師(X)
但編譯會很慢,斟酌XD
優化輸入輸出
幾乎必加
但就不能使用 scanf、printf