競賽程式介紹+演算法初探
9/21 複賽培訓[0]
327 王培軒
講師介紹
王培軒
- 110北市賽二等獎、全國賽24名==
- 327
- 乏善可陳
- 漠不關心資訊社事務
張庭瑋 aka ACOrz
- 芒國手
- CF: Alvingogo
- 2022 TOI 1!
- 110北市賽資訊一等獎、數學一等獎
- 110全國賽資訊三等獎
- TMO 3!
- 超級強
校隊培訓會有強強的學長(?
Disclaimer
這份簡報大量參考去年(2021)的校內培訓簡報,
主要是加入了一些我覺得不錯的東西,然後更新了一下資訊
競賽程式簡介
我想當校隊!
可是,校隊要幹嘛啊?
普通型高級中等學校數理及資訊學科能力競賽
9/12 校內初賽
10/4 校內複賽
11/20 北市賽
12月 全國賽
25人進複賽
11人校隊
9人進全國賽
10人進選訓
資訊奧林匹亞
12月 全國賽
3月初 入營考
3月底 一階
4月 二階
10人進選訓
20人+保障名額
12人進二階
4名國手
7月 IOI
入營考只要APCS實作3以上或通過海選就能考
我好想比賽!
只有能競跟資奧嗎?
好玩的比賽們
成大賽 | 8月 | 團體賽,停辦好幾屆了,今年復活! |
YTP | 8月 | 團體賽,好吃、有錢拿,前十幾名可以做專題, 舉辦的公司(精誠)很有錢 |
HP codewar | 11月 | 個人賽,這兩年線上賽很無聊(去年還撞全國賽),聽說實體有很多獎品可以拿,今年新增大專組 |
ISSC | ? | 團體賽,要去東海大學比,題目真的有點爛, 今年簡章不知為啥還沒出來 |
NPSC | 12月 | 題目非常好,往年也是很多獎品, 但去年只有線上賽跟獎狀 |
酷酷的線上比賽
我以為寫程式只要會語法...
演算法是什麼?
演算法:解決問題的方法
問題:可以被客觀定義、描述的問題
方法:可以被客觀描述的流程與步驟。大部分演算法應該對同一種類的問題都適用。
例:給定\( a,b \),求\( gcd(a,b) \),其中\(1 \leq a,b \leq 10^9 \)
演算法應該要在題目滿足條件的情況下,輸出正確的答案。
而在競賽程式中
競賽程式與一般軟體工程師做的事情有些不同:
- 問題、答案通常是以純文字輸入、輸出
- 程式運行的時間必須滿足限制,如時間、記憶體使用量等
- 由於競賽是限時的,因此通常不考慮寫個幾百行的程式,也不能使用一些擴充套件(但有些可以,之後會介紹)
- 若一個比賽有即時回饋的話,你會將程式碼上傳到Judge,然後Judge會以不同的測資測試你的程式是否能得到正確答案
我想變強!
要怎麼做...
教學網站
- Competition Handbook : 快速入門跟一些演算法 (英文)
- 2016 建中校內培訓講義 : 由簡入深,後面很難
- CP-Algorithms : 清楚明白,實作簡潔 (英文)
- OI Wiki : 中國網站,有點像CP-Algorithms,知識點廣
- USACO Guide : 有些比較難的神奇內容,編者水平很高
課程
題目
- TIOJ : 建中資訊社的judge
-
Zerojudge : 高中生程式解題系統
- Codeforces : 最大的 OJ/線上競賽平台
- Atcoder : 次大的 OJ/線上競賽平台
- CSES : 科技題大全
- CS Academy : 奇技淫巧大全
- oj.uz + OI Checklist : 後期vir OI用
演算法初探
複雜度分析
執行時間、空間消耗的估算
小問題
給你一個陣列\(a\),請問選兩個數字相減的最大值是多少?
哪個比較快?
int a[n];
int ans=-INF;
for(int i=0;i<n;i++){
for(int j=i-1;j>=0;j--){
ans=max(ans,a[i]-a[j]);
}
}
cout << ans << '\n';
int a[n];
int mx=-INF,mn=INF;
for(int i=0;i<n;i++){
mx=max(mx,a[i]);
mn=min(mn,a[i]);
}
cout << mx-mn << '\n';
運算次數
n=10 | 45次減法、45次取max | 10次取min、10次取max |
1000 | 499500/499500 | 1000/1000 |
1000000 | 5e11/5e11 | 1000000/1000000 |
無法接受
時間複雜度
左邊的程式有兩層迴圈,運算次數跟\(n^2\)成正比,右邊的程式則只有一層迴圈,運算次數跟\(n\)成正比。
我們就會說左邊的時間複雜度是\( \Omicron (n^2)\),右邊的是\( \Omicron (n)\)
複雜度
計算資源的消耗量(如時間、空間、計算次數)隨著問題規模增加,消耗量的增加方式
通常我們會用\( \Omicron (f(n))\)來表示,例如:\( \Omicron (n) \)、\( \Omicron (n^2) \)、\( \Omicron ( \log n) \)
(\( \Omicron \)為第15個希臘字母Omicron,只是大家都打\(O\))
可以簡單理解為只留最高次方的那項、並省略常數
建議大家不必細究嚴謹定義,等上大學在學><
時間複雜度與執行時間
n的大小(大概)
\(10\)
\(20\)
\(100 \sim 500\)
\(1000 \sim 5000\)
\(10000 \sim 10^5\)
\(10^5 \sim 10^6\)
\(10^6 \sim 10^7\)
\( \geq 10^9\)
對應的複雜度
\( \Omicron (n!) \)
\( \Omicron (2^n), \Omicron (n \times 2^n) \)
\( \Omicron (n^3) ,\Omicron (n^3 \log n) \)
\( \Omicron (n^2), \Omicron (n^2 \log n) \)
\( \Omicron (n \sqrt{n}) \)
\( \Omicron (n \log n) \)
\( \Omicron (n) \)
\( \Omicron ( \log n) \)
電腦每秒能夠運行大約\( 3 \times 10^8 \)個運算
北市賽Judge大概每秒\( 10^{10} \)
(參考用,實際上位元運算、加減比乘除、取模快很多)
空間複雜度
通常空間限制會是64MB~256MB不等。
每個int是4bytes,所以大概可以存\(10^7 \sim 10^8 \)個int
因此多半時候只要時間跑得完,空間就夠用
如果使用vector更是如此
因此我們只需在意時間複雜度
但真的只要數迴圈幾層就可以了嗎?
練習
int n;
cin >> n;
bool isprime = 1;
for (int i = 2;i*i <= n;i++) {
if (n % i == 0) isprime = 0;
}
cout << isprime << endl;
下列程式的時間複雜度是?
\( \Omicron ( \sqrt{n} ) \)
練習
int n;
cin >> n;
int a[n], pref[n];
for (int i = 0;i < n;i++) {
cin >> a[i];
pref[i] = a[i];
if (i > 0) pref[i] += pref[i-1];
}
int ans = 0;
for (int i = 0;i < n;i++) {
for (int j = 0;j < i;j++) {
ans = max(ans, pref[i] - pref[j]);
}
}
cout << ans << endl;
下列程式的時間複雜度是?
\( \Omicron ( n^2 ) \)
練習
int n;
cin >> n;
int cnt = 0;
while (n > 0) {
cnt++;
n /= 2;
}
cout << cnt << endl;
下列程式的時間複雜度是?
\( \Omicron ( \log n ) \)
練習
int n, k;
cin >> n >> k;
int a[n], pref[n];
for (int i = 0;i < n;i++) {
cin >> a[i];
pref[i] = a[i];
if (i > 0) pref[i] += pref[i-1];
}
int ans = 0, ind = 0;
for (int i = 0;i < n;i++) {
while (ind <= i && pref[i] - pref[ind] + a[ind] > k) {
ind++;
}
ans += i - ind + 1;
}
cout << ans << endl;
下列程式的時間複雜度是?
\( \Omicron (n) \)
練習
int n;cin >> n;
bool isprime[n];
for(int i=1;i<=n;i++) isprime[i]=1;
for(int i=2;i<=n;i++){
for(int j=2*i;j<=n;j+=i) isprime[j]=0;
}
for(int i=2;i<=n;i++) if(isprime[i]) cout << i << '\n';
下列程式的時間複雜度是?
\( \Omicron (n \log n) \)
時間複雜度有時候需要依靠一些性質才能算出!
沒那麼簡單!
deck
By peter940324
deck
- 902