競技程式設計 #2

講義 & 題目:petingo.me/cp/

要幹嘛

  • 投影片、題目、題解:petingo.me/cp/
  • 先跟大家說:
    • ITSA 開始報名了
    • 中區開始報名了
    • 交大寒訓開始報名了
    • CPE 開始報名了
  • 上次的題解
  • 基礎資料結構
  • STL

上次的題解

  • 共筆連結
  • 覺得我寫錯或是有東西想補充歡迎一起共筆:)

基礎資料結構

  • Array
  • Linked list
  • Stack
  • Queue
  • Graph
  • Tree
  • Heap

Array & Linked List

Array & Linked List

queue

  • 排隊
  • 先進先出
  • 人類

stack

  • 先進後出
  • 腔腸動物門
  • 刺胞動物門)

Graph

  • 用來記錄關聯、關係的東西。
  • 點 vertex
  • 邊 edge

Graph

  • 博大精深
  • 有向圖、無向圖
  • 有環、無環(從自己出去有沒有辦法走回來)

如何表示一張圖?

Adjacency Matrix

如何表示一張圖?

Adjacency List

tree

  • 無環圖
  • binary tree

tree

  • 用 struct
template<class T>
struct Node<T>
{
    T value;
    struct Node<T>* parent;
    vector<struct Node<T>*> children;

};

tree

  • 用一個陣列來存

DFS、BFS

  • 深 Deep First Search:2, 7, 2, 6, 5, 11, 5, 9, 4
  • 廣 Breadth First Search:2, 7, 5, 2, 6, 9, 5, 11, 4

C++ STL

  • Container
    • array
    • string
    • vector
    • deque
    • list
  • Iterator
  • Container Adapter
    • stack
    • queue
    • priority_queue
    • set
    • map
    • bitset

string

  • #include <string>
  • 字串
  • 跟 char[] 87% 像但
    • 能屈能伸
    • 會自動補 '\0'

string

#include <string>
using namespace std;
int main(){

    string s = "meow";

    s[2]; // o

    s.length(); // 4
    s.size();   // 4

    s = s + s;  // meowmeow

    s < "woof"; //true
    
    cin >> s;
    cout << s;

}

vector

vector

  • vector<T> v;
  • v.push_back(T object)
  • v.pop_back()
  • v[i]
  • v.front()
  • v.back()
  • v.size()
  • v.empty()
  • v.clear()
  • 宣告
  • 在最後面新增
  • 把最後面刪掉
  • 第 i 項(跟一般陣列一樣)
  • v[0]
  • v[v.size()-1]
  • 取得 vector 的大小
  • 空的?
  • 清空

vector

  • 範例
#include <vector>
using namespace std;
int main(){
    vector<int> v;        // empty
    v.push_back(3);       // 3
    v.push_back(4);       // 3, 4
    v.push_back(5);       // 3, 4, 5
    v[0] = v[1] - v[0];   // 1, 4, 5 (v[0] = 4 - 3)
    v.size()              // size = 3
    v.pop_back();         // 1, 4
    v.size();             // size = 2
    v.clear();            // 清空!
    v.empty();            // true
}

deque

  • include <deque>
  • 雙頭龍
  • 可以操作頭的 vector
  • 但時空間複雜度很爛,沒事不要用

deque

  • deque<T> d;
  • d.push_back(T object)
  • d.push_front(T object)
  • d.pop_back()
  • d.pop_front()
  • d.front()
  • d.back()
  • d.size()
  • d.empty()
  • d.clear()
  • 宣告
  • 在最後面新增
  • 在最前面新增
  • 把最後面刪掉
  • 在最前面新增
  • d[0]
  • d[d.size() - 1]
  • 取得 vector 的大小
  • 空的?
  • 清空

list

  • #include <list>
  • double linked list
  • 操作跟 deque 一樣
  • 但多了
    • insert(iterator, object)

    • erase(iterator)

    • erase(first, last)

iterator

  • 迭代器
  • 可以想像成很厲害的指標

iterator

  • Container<T>::iterator iter;
  • ++、-- 指向下一個、前一個元素
  • == 兩個是否指到同一個
  • iter + 5 後面 5 個元素
  • iterA - iterB 算出兩個差幾個

iterator

// vector 基本用法範例
#include <iostream>
#include <vector>
using namespace std;
int main() {
  vector<int> v;      // 宣告 v
  v.push_back(10);    // v = {10}
  v.push_back(20);    // v = {10, 20}
  v.push_back(30);    // v = {10, 20, 30}

  vector<int>::iterator iter;       // 宣告 iterator

  // v.begin() 指向 v[0] iterator
  // v.end()   指向 v[size()] ** 注意 v[size()] 其實是沒東西的!
  // iter++    讓 iterator 指向下一個元素

  for (iter = v.begin(); iter != v.end(); iter++) {
    // iter 類似指標,所以如果要取直要加 *
    cout << *iter << endl;
  }
  
  cout << v.end() - v.begin() << endl; // 輸出 3

}

foreach + auto

#include <iostream>
#include <vector>
using namespace std;
int main() {
  vector<int> v;
  v.push_back(10);
  v.push_back(20);
  v.push_back(30);

  for (auto i : v) {
    cout << i << endl;     // 這邊不用加 *
  }
}
  • 遍歷容器的另一種方法 C++11

stack

  • #include <stack>
  • 預設是用 deque 實作,所以時空間複雜度很爛
  • 可以用 vector 取代,或是在宣告的時候指派
    • stack<T, C>

    • C 為要使用的容器,可為 vector、list 或 deque

    • stack<int> s;

    • stack<int, vector<int>> s;

stack

  • stack<T> s;
  • s.push(T object)
  • s.pop()
  • s.top()
  • 宣告
  • 放進去
  • 拿出來
  • 取得頂端

queue

  • queue<T> q;
  • q.push(T object)
  • q.pop()
  • q.front()
  • q.back()
  • 宣告
  • 丟東西進去(在後面)
  • 把頭丟掉

priority_queue

  • #include <queue>
  • 用 max heap 實作
  • 但 priority queue 裡的東西都是排序好的
  • 只能拿優先序最高的東西出來
  • 放東西跟丟東西都是 log(n)

priority_queue

  • priority_queue<T> pq;
  • pq.push(T object)
  • pq.pop()
  • pq.top()
  • 宣告
  • 丟東西進去
  • 把最大的丟掉
  • 拿最大的

priority_queue

  • 當需要自定義的排序方法
  • priority_queue<T, C, cmp> pq

    • T 型態

    • C 容器

    • cmp 比較方法

  • priority_queue<int, vector<int>, grater<int>> pq

priority_queue

#include <bits/stdc++.h>
using namespace std;
// 1 3 2 9 9 8 1
int main() {
  priority_queue<int, vector<int>, greater<int>> pq;
  pq.push(1);
  pq.push(3);
  pq.push(2);
  pq.push(9);
  pq.push(9);
  pq.push(8);
  pq.push(1);
  while (!pq.empty()) {
    cout << pq.top() << " ";
    pq.pop();
  }
}

priority_queue

  • 當需要採用自定義的 class
  • 重寫 cmp()

  • 或是重載 operator<

  • 重載 < const 要加好,不然會噴錯

// work
bool operator<(const MyClass &other) const {
    return key < other.key;
}
// not work
bool operator<(MyClass &other) const {
    return key < other.key;
}
// not work
bool operator<(const MyClass &other){
    return key < other.key;
}

priority_queue

// 一坨貓貓,用年齡越大的優先序越高
#include <bits/stdc++.h>
using namespace std;
class Meow {
 public:
  string name;
  int age;
  Meow() {}
  Meow(string n, int a) { name = n, age = a; }
  bool operator<(const Meow &other) const { return age < other.age; }
};

int main() {
  priority_queue<Meow> pq;

  pq.push(Meow("Amy", 999));
  pq.push(Meow("Bob", 3));
  pq.push(Meow("Cathy", 777));
  pq.push(Meow("Dick", -1));

  while (!pq.empty()) {
    cout << pq.top().name << " : " << pq.top().age << endl;
    pq.pop();
  }
}

pair

  • #include <utility>
  • (1, 3)
  • ("cat", 4)

pair

  • pair<Ta, Tb> p

  • p.first

  • p.second

// 創造一個 (3, 4) 的 pair,然後印出第一個、第二個
// 輸出結果:3 4
pair<int, int> a(3, 4);
cout << a.first << endl;
cout << a.second << endl;

pair

  • make_pair(a, b);

  • 自動生成對應形別的 pair

  • make_pair(5, 3)  // pair<int, int>

  • make_pair("meow", 3)  // pair<string, int>

pair

  • pair<int, int> a, b;

  • a < b?

  • 先比 first、再比 second

map

  • #include <map>
  • Key - value
    • 上品 - 3
    • 井太郎 - 10
  • 用起來有點像是 Key 不是正整數的陣列
  • Key 唯一
  • 實作是自動平衡的紅黑樹
  • 裡面會照 Key 排序
  • 插入、查找都是 log(n)

map

  • map<Key, Value> m;

  • m[Key] = Value;

  • m.insert(pair<Key, Value>)

  • m.erase(Key)

  • m[Key]

  • m.find(Key)

  • 宣告
  • 插入
  • 插入
  • 刪掉
  • 取 value
  • 取出 (Key, value) 的 pair iterator,Key 不存在會回傳 m.end()

map

#include <bits/stdc++.h>
using namespace std;
int main() {

  map<string, int> score;

  score["B10532026"] = 100;
  score["B10538888"] = 60;
  score["B10599123"] = 88;

  cout << score["B10532026"] << endl;
  cout << (*score.find("B10532026")).second << endl;
  
}
  • 範例(學號,分數)

map

  • 找東西是否存在有兩種方法

  • if(m[key] != 0) // bad

    • m[key]會在沒有key自動生出新的元素

    • 導致map內的元素個數增加

 

  • if(m.find(key) != m.end()) // good

    • m.find並不會增加map內的元素個數

set

  • #include <set>
  • 沒有 value 的 map
  • 一樣是紅黑樹
  • 當東西只會出現一次的時候很好用

set

  • set<T> s;

  • s.insert(T object)

  • s.erase(T object)

  • s.erase(iterator)

  • 宣告
  • 插入
  • 刪掉
  • 刪掉

set

// 範例:把 1 1 4 4 3 1 5 4 4 3 這個序列塞進 set
// 然後把 3 刪掉
// 輸出結果為 1 4 5
#include <bits/stdc++.h>
using namespace std;
int main() {
  set<int> s;
  s.insert(1);
  s.insert(1);
  s.insert(4);
  s.insert(4);
  s.insert(3);
  s.insert(1);
  s.insert(5);
  s.insert(4);
  s.insert(4);
  s.insert(3);
  s.erase(3);

  for (auto i : s) {
    cout << i << endl;
  }
}

unordered map/set

  • 跟一般的 map / set ㄧ樣
  • 應該是用 hash table 做
  • insert / erase 都是 O(1)

CP-1205

By Petingo

CP-1205

  • 466