第二屆 清大暑期程式競賽集訓營
105
上課教室
104
休息教室
廁所
廁所
廁所
販買機
(地下室)
中庭
ubike站(解散地點)
台達館
資電館
早上 08:00~09:00提供早餐
晚上 09:30~10:00結束活動
ACM-ICPC 2017 泰國第 7
TOI 2!
略玩CTF
熟悉程式語言:
C++ ⭐⭐⭐(從國中寫到現在)
PHP ⭐⭐⭐(寫玩具用)
Python3 ⭐(學好玩的?)
日月卦長
你們的前營長
清大資工程式大帝程式設計競賽 銀牌獎
2014 成功大學暑期高中生程式設計邀請賽 第一名
ACM ICPC 越南河內站三等獎
ACM ICPC Manila Sinag Award
第五屆ITSA 第七名
2017 兩岸清華程式競賽 第三名
2017 Taiwan Online Programming Contest 第五名
International Symposium on Physical Design 2018 第三名
ICCAD 2017 Problem B. 佳作
TOI 1階
清大資工競技程式設計助教
資訊之芽培訓計畫清大講師
•清大資工大二
•竹區資訊之芽培訓計畫清大負責人
•台清交程式設計大賽第9名
•2018 ITSA 桂冠賽 挑戰組 佳作
•2018 程式大帝 佳作
•2018 兩岸清華程式競賽佳作
詹振宏
台清交程式競賽 第九名
ISSC 2015 亞軍
2015 青年程式設計競賽 第一名
2015 成大暑期高中生程式競賽 第三名
NPSC 某年 高中組 第9名來著
演算法是什麼?
[al-guh-rith-uh m]
不同型式的資訊競賽
Text
Text
Text
Text
C++ | 最常使用的解題語言 |
---|---|
C | 通常從大學才學程式設計的人使用 |
Java | 解特殊題型 (大數運算) |
Python 3 | 解特殊/簡單題型 (大數運算) |
- NTHU ACM 比賽團隊
使用者名稱@電腦名稱:目錄$
使用`pwd`指令可以看目前的目錄名稱
使用`ls`指令可以看目錄裡有什麼檔案
桌面的資料在 Desktop
使用`cd 目錄`指令可以進入目錄
其他常見的編譯器有:Clang (Mac, 部分linux使用), MSVC
指令:`g++ C++檔案 -o 程式檔`
如果沒有設定 `-o 程式檔` 預設會是a.out
指令:`./程式檔`
注意要有 ./
除了單純的使用之外,通常會附加一些參數來增加功能
-std=c++XX
C++的版本是最需要注意的選項,設定錯誤可能會導致在不同電腦上編譯錯誤
-Wall -Wextra
讓編譯器顯示更多的錯誤
開起來經常得可以找出意想不到的BUG
int a = -1;
unsigned b = 0;
if( a < b )
cout<<"AC!";
else
cout<<"You dead!";
g++ a.cpp
./a.out
You dead!
輸出
int a = -1;
unsigned b = 0;
if( a < b )
cout<<"AC!";
else
cout<<"You dead!";
g++ -Wall -Wextra a.cpp
輸出
-OX
g++ 可以盡量的在規範內加速你的程式
一般比賽與應用上,都會使用O2優化
開啟優化後,可能會讓未定義行為造成的問題更明顯,讓程式更容易debug
開啟一個文字檔案,在開頭打上`#!/bin/sh`存檔
這個檔案就是一個Script
存檔之後,要設定這個檔案是可以執行的
#!/bin/sh
rm a.out # 刪除舊的程式檔案
g++ -std=c++11 -O2 -Wall -Wextra -O2 $1
./Script名稱 CPP檔案名
程式題目哪裡最重要?
A. 問題描述
B. 輸入與輸出格式
D. 資料大小
C. 範例測資
矩陣是將一群元素整齊的排列成一個矩形...
第一行有三個正整數 R, C, M ...
3 2 3
0<R, C, M <10
- 某出題者的評論
第一題
分數(0~100 間)
人數為 1~20 的整數
第二題
三個介於 1~10 之間的正整數
第三題
\(N < 10000\) ,\( 0 \leq L , R < 10000000 \)
\(2 \leq N \leq 100000\)
第四題
如果\(N=100\),使用泡沫排序法,程式在1秒內跑得完嗎?
如果\(N=10000\),使用泡沫排序法,程式在1秒內跑得完嗎?
如果\(N=10000\),使用快速排序法,程式在1秒內跑得完嗎?
如果\(N=10000000\),使用快速排序法,程式在1秒內跑得完嗎?
時間複雜度
int mx = a[0];
for(int i=1;i<N;++i)
{
mx = max( mx , a[i] );
}
如果\(f(x)\in \mathcal{O}(g(x))\)
就存在\(c\)與\(x_0\)
如果\(x>x_0\)
\(c\times g(x)\geq f(x)\)
如果\(f(x)\in \mathcal{O}(g(x))\)
如果\(f(x)\in \mathcal{O}(g(x))\)
就存在\(c\)與\(x_0\)
\(x_0\)
\(c\times\)
如果\(f(x)\in \mathcal{O}(g(x))\)
就存在\(c\)與\(x_0\)
如果\(x>x_0\)
\(x_0\)
\(c\times\)
\(x\)
如果\(f(x)\in \mathcal{O}(g(x))\)
就存在\(c\)與\(x_0\)
如果\(x>x_0\)
\(c\times g(x)\geq f(x)\)
\(x_0\)
\(c\times\)
\(x\)
\(c\times g(x)\)
\(f(x)\)
如果\(x\geq1\)
\(x^2-x+2\)
\(\leq x^2+x+2\)
\(\leq x^2+x^2+2x^2=4x^2\)
如果\(x\geq1\)
\(x^2-x+2\)
\(\leq x^2+x+2\)
\(\leq x^2+x^2+2x^2=4x^2\)
設\(c=4,x_0=1\)
如果\(x_0\leq x\)
\(x^2-x+2\leq cx^2\)
因此
\(x^2-x+2 \in O(x^2)\)
\(f(x)\in \mathcal{O}(x^n)\)
// Find max value index in array
int index = -1;
for(int i=0;i<N;++i)
{
bool flag = true;
for(int j=0;j<N;++j)
{
if( a[j] > a[i] )
flag = false;
}
if( flag )
index = i;
}
如果一個演算法複雜度是\(O(x^2)\),表示該方法最糟不會比所有\(O(x^2)\)的方法糟糕
// Find max value index in array
int index = 0;
for(int i=1;i<N;++i)
{
if( a[i] > a[index] )
index = i;
}
\(O(x^5)\)
\(O(x^3)\)
\(O(x)\)
\(O(\log n!)=O(n\log n)\)
把題目測資上限\((N=20000)\)代入
如果大於\(10^8\),基本上就會TLE
scanf/printf
cin/cout
不論喜歡用何種方法,至少要熟悉一套
cin
getline
鍵盤輸入
檔案輸入
串流輸入
C++中只要知道串流怎麼操作
就能處理任意能接上串流的資料
標準輸入STDIN
標準輸出STDOUT
cout
鍵盤輸出
檔案輸出
串流輸出
int a;
double b;
cin>>a>>b;
./a.exe < input.txt
int a = 1234;
cout >> a >> endl;
./a.out > answer.txt
./a.out < test.in > res.out # 可以一起用
int a;
scanf("%d", &a);
printf("%d", a);
C語言無法自動判斷型態,要透過%d等標記來協助
float | double | long double | |
---|---|---|---|
scanf | f | lf | Lf |
printf | f | f | Lf |
printf(C++11) | f | lf, f | Lf |
scanf/printf語言的標記在不同標準上會有差異
Ctrl+Z
int a;
while( cin>>a )
{
//輸入成功就重複執行
}
int a;
while( scanf("%d", &a) != EOF )
{
//輸入成功就重複執行
}
C語言的字串為使用 \0 結尾的字元陣列
char str[100] = "C Style String";
C++語言的字串為string物件
string str = "C++ string";
C++的字串可以自動控制長度,可以減少陣列長度不夠的問題
C語言的字串若善加使用,效率比C++還要好
C | C++ | |
---|---|---|
C字串 | fgets | cin.getline |
C++字串 | getline |
char buf[100];
cin.getline(buf, 100);
這程式可以讀幾個字元?
陣列開大一點,不會有問題;陣列開小一點,絕對會出事
int a;
char buf[10];
cin >> a;
cin.getline(buf, 10);
cout << a << "," << buf << "<";
123
abc
輸入
輸出
123,<
int a;
cin >> a;
1 | 2 | 3 | \n | a | b | c |
---|
cin 開始讀取整數給a
int a;
cin >> a;
2 | 3 | \n | a | b | c |
---|
cin 開始讀取整數給a
1 |
---|
int a;
cin >> a;
3 | \n | a | b | c |
---|
cin 開始讀取整數給a
2 |
---|
int a;
cin >> a;
\n | a | b | c |
---|
cin 開始讀取整數給a
3 |
---|
int a;
cin >> a;
\n | a | b | c |
---|
cin 發現不是數字,停止讀取
\n |
---|
getline(buf, 10);
\n | a | b | c |
---|
cin.getline開始讀取
getline(buf, 10);
a | b | c |
---|
cin.getline讀取到換行,直接結束
\n |
---|
int a;
char buf[10];
cin >> a;
cin.get(); // 讀一個字元
cin.getline(buf, 10);
cout << a << "," << buf << "<";
123
abc
輸入
輸出
123,abc<
int a;
char buf[10];
cin >> a;
cin.getline(buf, 10);
cout << a << "," << buf << "<";
123 abc
輸入
輸出
123, abc<
string str;
getline(cin, str);
cout << str ;
參數的設計有點詭異
char buf[10];
fgets(buf, 10, stdin);
cout << buf << "X";
abcd1234\n
輸入
輸出
abcd1234
X
char buf[10];
fgets(buf, 10, stdin);
// 如果沒有換行符號會RE!
*strchr(buf, '\n') = 0;
cout << buf << "X";
abcd1234\n
輸入
輸出
abcd1234X
strchr : 找到字串中某個字元的位置
在一行裡面有很多個數字,你能把數字都加起來嗎?
123 456 789
1 2 3 4 5 6 7 8 9 10
11
1368
55
11
範例輸入
範例輸出
不要在輸入函數上做太多動作
string buf;
while( getline(cin, buf) )
{
}
"123 456 789"
#include<sstream>
std::stringstream ss;
#include<sstream>
std::stringstream ss;
ss << "1234" << ' ' << 5678;
//ss現在為 "1234 5678"
ss << "1234" << ' ' << 5678;
//ss現在為 "1234 5678"
int a;
ss >> a; // a = 1234, ss現在為 " 5678"
ss >> a; // a = 5678, ss現在為 ""
string buf;
stringstream ss;
while( getline(cin, buf) )
{
ss.clear(); // 記得初始化
ss.str(buf); // 直接設定串流裡面的資料
int tmp, ans = 0;
while( ss >> tmp ) ans+=tmp;
cout << ans;
}
真相是什麼?
std::flush
清除輸出串流內的資料
如果操作的對象是檔案,那這個策略是必要的
因為硬碟動一次的代價很大,不一次做多一點事會很浪費時間
std::flush
大部分的OJ都會把標準輸出變成檔案輸出,讓問題更加明顯
可是哪裡用了flush ?
#include<bits/stdc++.h>
using namespace std;
int main()
{
int a;
while( cin >> a )
{
cout << a << endl;
}
}
哪裡用到了flush ?
根據C++的標準,用一次cout<<endl等於
cout << '\n' << flush;
從今天開始,不要亂用endl,用 '\n'代替它
如果不用這個同步功能,可以把它關掉
ios::sync_with_stdio(false);
// 關掉後就不可以cout / printf 混用
因為OJ不會即時的看答案對不對,因此不需要這個功能
因此可以取消這功能
cin.tie(0);
// 關掉後 cout的資料就不一定會立刻顯示
#include<bits/stdc++.h>
using namespace std;
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
int a;
while( cin >> a )
{
cout << a << '\n';
}
}
stdio.h | cstdio |
string.h | cstring |
stdlib.h | cstdlib |
ctype.h | cctype |
XXX.h \(\Rightarrow\) cXXX
Undefined Behavior
cout << 1 / 0 << endl;
int x = 2147483647; //int 最大值
if( x+1 > x )
cout << "Ya!" << endl;
else
cout << "溢位" << endl;
int 加超過範圍會變成負數,正確嗎?
if( x+1 > x )
當x不是最大值時,判斷式成立 ( true )
當x是最大值時,雖然二補數下+1會變負數,但是C++規定int計算超出範圍是未定義行為,數值未定。因此編譯器為了優化,可以認為此時 x+1 會大於 x ( true )
if( x+1 > x )
cout << "Ya!";
else
cout << "溢位";
if( true )
cout << "Ya!";
else
cout << "溢位";
cout << "Ya!";
01000000010010001111010111000011
若能避免使用小數,就應該避免
否則要小心處理誤差的問題
double x;
cin >> x;
if( x==5 ); 容易出問題
if( std::abs( x - 5 ) < 1E-6 ); 要容許一點誤差
C=243, A=3, B=?
double A, C;
cin >> C >> A;
int B = log(A) / log(C);
cout << B;
243 3
輸入
輸出
4
log(243)/log(3) = 4.99999999999999911182158029987
計算結果比原來數字小了一點
轉成int,小數部分全部變不見
資料數量 | |
---|---|
struct/class | 任意多 |
std::pair | 2 |
std::tuple | 任意多 |
//定義
struct Point{
int x;
int y;
string name;
};
//宣告
Point data, array[666];
//使用變數
data.x = data.y = array[2].x = 7122;
data.name = "StarStar";
struct Info{
string name;
Info(){ //建構子
name = "7122";
}
};
Info data; //會呼叫建構子
cout<< data.name ;
Output:
7122
struct Point{
int x;
int y;
Point(int a,int b){ //建構子
x=a; y=b;
}
};
Point data(94,87); //會呼叫建構子
cout<< data.x << ' ' <<data.y ;
Output:
94 87
struct Point{
int x;
int y;
Point(int a,int b){ //建構子
x=a; y=b;
}
};
Point data; //ERROR!
Output:
Compile Error!
Can not find Point()
struct Point{
int x;
int y;
Point(int a,int b){ //建構子
x=a; y=b;
}
Point(){ //建構子 called
x=1; y=2;
}
};
Point data;
cout<<x<<' '<<y;
Output:
1 2
using pis = pair<int,string>; //C++11
pii data;
data.first = 123;
data.second = "ABC";
data = {123,"ABC"};
data = make_pair(123,"ABC");
using pii = pair<int,int>;
pii a[3] = { {2,1},{1,2},{3,0} };
sort( a,a+3 );
for( auto v:a )
cout<<v.first<<' '<<v.second<<endl;
Output:
1 2
2 1
3 0
using piii = pair<int,pair<int,int>> ; //C++11
using piii2= pair<int,pair<int,int> > ;
piii a;
a.first = a.second.first = a.second.second = 87
#include<utility> //also in algorithm
using tiii= tuple<int,int,int>;
tiii a;
a = {1,2,3}; // C++14 and g++ >= 6
a = make_tuple(1,2,3); // Always OK
using tiii= tuple<int,int,int>; //C++11
tiii a = {1,2,3};
auto [i,j,k] = a; //C++17
int i,j,k;
tie(i,j,k) = a; //Always OK
i = get<0>(a);
j = get<1>(a);
k = get<2>(a); //Always OK
#include<vector>
vector<int> arr;
arr.resize(N); //設定大小為N
arr[0] = arr[N-1] = 1; //與一般陣列一樣
int a;
cin>>a;
int arr[a]; // Error!
int a;
cin>>a;
vector<int> arr(a);
vector<int> v;
v.push_back(1);
v.emplace_back(2); // C++11
for(int i:v) cout<<i<<' ';
Output:
1 2
push_back( 我是資料 )
我是資料 (2)
複製
我是資料 |
---|
複製
push_back要複製資料2次
emplace_back( 我是資料 )
直通終點
我是資料 |
---|
複製
emplace_back要複製資料2次
vector<tuple<int,int,int>> v;
v.push_back(make_tuple(4,5,6));
//只有emplace_back可以這樣縮減歐~
v.emplace_back(7,8,9);
for(auto [i,j,k]:v) //C++17
cout<<i<<' '<<j<<' '<<k<<endl;
Output:
1 2 3
4 5 6
7 8 9
emplace_back的括號可以當作是呼叫建構子