競技程式設計 #1

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

今天ㄉ內容

  • Judge 介紹
  • 競賽常見的輸入輸出
  • 時間複雜度
  • 二分搜尋法
  • C++ STL-演算法

Judge 介紹

練習ㄉ地方

  • Zerojudge 高中生程式解題系統
    • 很多中文題但難度懸殊
    • 有些 UVa 的翻譯題
  • UVa
    • 搭配中国北京刘汝佳的书
    • 可以搭配 Lucky Cat 翻譯
    • 基本上直接 Google 題目都可以找到解答
  • TIOJ
    • 建中校內培訓講義
  • Virtual Judge

Virtual Judge

速速點雞加入ㄅ

(´・ω・`)

修幹ㄉ地方

  • Codeforce
  • AtCoder
  • CS Academy

怎麼練習呢?

  • 看懂題目
  • 想方法
  • 開始寫 code
  • 上傳
  • 等待 Judge 回應
    • 根據 Judge 的回應修改

Judge 的回應

  • AC (Accept):通過
  • WA (Wrong Answer):答案錯誤
    • ​超出變數大小?
      • int ≈
    • 例外狀況?
  • TLE (Time Limit Exceed):超過時間限制
    • 演算法的時間複雜度太高了!(就是太爛ㄌ)
    • 卡 I/O?

$$10^{10}$$

Judge 的回應

  • RE (Runtime Error):執行時錯誤,通常是記憶體存取錯誤
    • ​int a[10]; a[11];
  • CE (Compile Error):表示編譯錯誤
    • ​C++ 版本?

競賽常見的輸入輸出

C++ 基本

時間複雜度

演算法

  • 解決問題的方法與步驟
  • 同樣的問題可能有多種解決方案
    • 選擇效率比較好的那一個
    • 花費時間短、所需記憶體空間小
  • 大部分的情況我們比較在乎「時間」
    • 如何評估?怎麼表示?

時間複雜度

  • 程式最多會跑多少指令
  • Big-O
    • O(g(n)) = f(n)
    • 存在正實數 c, N
    • 使得對於任意正實數 n ≧ N
    • 0 ≦ f(n) ≦ cg(n) 成立

舉例來說

  • 求 1~n 的和,當 n = 100 時?當 n = 10000 時?

時間複雜度

  • 求 1~n 的和,當 n = 100 時?當 n = 10000 時?
  • 跑幾次?
int n = 100;
int sum = 0;
for( int i = 1 ; i <= n ; i++ ){
    sum += i;
}
int n = 100;
int sum = (1 + n) * n / 2;

時間複雜度

  • 求 1~n 的和,當 n = 100 時?當 n = 10000 時
  • 跑幾次?
int n = 100;
int sum = 0;
for( int i = 1 ; i <= n ; i++ ){
    sum += i;
}
int n = 100;
int sum = (1 + n) * n / 2;

$$n 次\rightarrow O(n)$$

$$1 次\rightarrow O(1)$$

時間複雜度

$$O(1)$$

$$O(log(n))$$

$$O(n)$$

$$O(n log(n))$$

$$O(n^2)$$

$$O(n^3)$$

$$O(2^n)$$

$$O(n!)$$

最快

最慢

時間複雜度

$$O(1)$$

$$O(log(n))$$

$$O(n)$$

$$O(n log(n))$$

$$O(n^2)$$

$$O(n^3)$$

$$O(2^n)$$

$$O(n!)$$

一秒大約可以跑 $$10^7~10^9$$

$$無限大$$

$$n = 10^{1?}$$

$$n = 10^7$$

$$n = 100,000$$

$$n = 1000$$

$$n = 100$$

$$n = 20$$

$$n = 12$$

常見

時間複雜度

O(1)

  • 不管 n 是多少只要一次
  • (1 + n) * n / 2
  • a[5]

O(n)

  • 一個 for 迴圈
int n = 100;
int sum = 0;
for( int i = 1 ; i <= n ; i++ ){
    sum += i;
}

O(n^2)

  • 兩層 for
24 3 7 14 9
23 20 22 5 19
13 2 8 4 10
1 15 6 25 21
16 12 17 18 11
int n = 5;
int matrix[n][n];
for( int i = 0 ; i < n ; i++ ){
    for( int k = 0 ; k < n ; k++ ){
        if( matrix[i][k] == 7 ){
            ...
        }
    }
}

O(log(n))

  • 以 2 為底的 log
  • 一次切一半
  • 經典例子:binary search tree

阿如果不固定的話呢?

  • 在 52 張撲克牌裡找紅心 A
  • 一個 for 迴圈
  • 可能第一張就找到了啊?
    • 第一張就找到 O(1)
    • 最後一張才找到 O(n)
    • 平均來說 O(n/2)
    • 根據定義 O(n/2) = O(n)
int n = 52;
for( int i = 0 ; i < n ; i++ ){
    if( poker[i] == "紅心A" ){
        return i;
    }
}

阿如果不固定的話呢?

  • 找兩次的話呢?
    • O(2n) = O(n)
int n = 52;
for( int i = 0 ; i < n ; i++ ){
    if( poker[i] == "紅心A" ){
        ...
    }
}

for( int i = 0 ; i < n ; i++ ){
    if( poker[i] == "紅心K" ){
        ...
    }
}

各種組合的話呢?

  • 一層 for + 兩層 for
  •  
int n = 100;
for( int i = 0 ; i < n ; i++ ){
    ...
}

for( int i = 0 ; i < n ; i++ ){
    for( int i = 0 ; i < n ; i++ ){
        ...
    }
}

$$O(n + n^2) = O(n^2)$$

所以說

  • 就是最大的那一個

$$O(2n) = O(n)$$

$$O(n^2 + 3n) = O(n^2)$$

$$O(n+log(n)+n log(n)) = ?$$

二分搜尋法

尋找紅心 A

  • 在一串陣列裡面找東西
    • 寫個 for 一個一個比對
    • O(n)

假如資料有單調性

  • 猜數字?
    • 1~100,我先在心中想一個數字,你猜一個數字後我會跟你說答案比它大還是比它小。
    • 第一個數字猜多少比較好?

假如資料有單調性

  • x = 24
  • 50 => 1 ~ 49 
  • 25 => 1 ~ 24
  • 12 => 13 ~ 24
  • 18 => 18 ~ 24
  • 21 => 22 ~ 24
  • 23 => 24 ~ 24
  • 24 => bingo

假如資料有單調性

  • 其實猜數字就是在一個 1~100 排序好的陣列裡面,找一個特定的數字

Binary Search

$$O(log(n))$$

  • 找 7

Binary Search

#include <cstdio>
int main() {
  int array[] = {1, 3, 4, 6, 7, 8, 10, 13, 14, 18, 19, 21, 24, 37, 40, 45, 71};
  int l = 0, r = 16;

  while (l != r) {
    int mid = (l + r) / 2;
    if (array[mid] == 7)
        r = l = mid;
    else if (array[mid] >= 7)
        r = mid;
    else
        l = mid + 1;
  }
  
  printf("%d\n", l);
}

除了找東西

  • 數值逼近
  • 練習題 :)

C++ STL 演算法

STL 是啥東

  • 標準模板庫Standard Template Library)
    
  • 包含演算法、一些常用的資料結構

STL - algorithm

  • #include <algorithm>
    • bsearch()
    • sort()
      • 由小排到大
      • quick sort
      • 不穩定排序
    • stable_sort()
      • merge sort​
    • reverse()
      • 1, 5, 3, 9 => 9, 3, 5, 1

$$\rightarrow nlog(n)$$

$$\rightarrow nlog(n)$$

stable vs. unstable

店名 評價
井太郎 8
自助餐 2
溫州 8
上品 8
地中海 6
店名 評價
自助餐 2
地中海 6
上品 8
溫州 8
井太郎 8
  • 有多組同樣評價的東西,誰在前、誰在後?

stable

店名 評價
井太郎 8
自助餐 2
溫州 8
上品 8
地中海 6
店名 評價
自助餐 2
地中海 6
井太郎 8
溫州 8
上品 8
  • 原本在前面的排序完也應該在前面

unstable

店名 評價
井太郎 8
自助餐 2
溫州 8
上品 8
地中海 6
店名 評價
自助餐 2
地中海 6
上品 8
溫州 8
井太郎 8
  • 隨便啦沒差(大部分的時候都沒差)

來實作!

sort()

  • 由小排到大
#include <algorithm>
using namespace std;

int main() {
    int array[7] = {5, 2, 3, 6, 4, 4, 9};
    sort(array, array + 7);

    // 2, 3, 4, 4, 5, 6, 9
}

sort() + reverse()

  • 大排到小?
#include <algorithm>
using namespace std;

int main() {
    int array[7] = {5, 2, 3, 6, 4, 4, 9};
    sort(array, array + 7);      // 先由小到大排序好
    reverse(array, array + 7);   // 再反轉

    // 9, 6, 5, 4, 4, 3, 2
}

sort() + compare()

  • 其實 sort() 可以傳三個參數
#include <algorithm>
using namespace std;

bool compare(int a, int b){
    return a > b;
}

int main() {
    int array[7] = {5, 2, 3, 6, 4, 4, 9};
    sort(array, array + 7, compare);

    // 9, 6, 5, 4, 4, 3, 2
}

sort() + greater<int>()

  • 需要重造輪子ㄇ?
#include <algorithm>
#include <functional>
using namespace std;

int main() {
    int array[7] = {5, 2, 3, 6, 4, 4, 9};
    // sort(array, array + 7, less<int>());
    sort(array, array + 7, greater<int>());

    // 9, 6, 5, 4, 4, 3, 2
}

如果要排一組資料呢?

  • 對餐廳的評價排名
    • 名稱
    • 評價
  • 寫成 class 並排序

sort() + class

#include <algorithm>
using namespace std;

class Restaurant {
 public:
  string name;
  int rank;

  bool operator<(const Restaurant &other) const {
    return rank < other.rank;
  }

};
  • 自己寫 compare()
  • 重載運算子 <
  • 整份 code:gist

來練習ㄅ!

CP-1128

By Petingo

CP-1128

  • 228