Lecturer:我才不在乎梵諦岡的袋鼠
Repkironca、阿蘇
圈內人數數都是 zero-based
田鼠這個菜逼八寫錯了 🐢🐢🐢
不要懷疑,這真的是第二節
還敢教那麼快啊 BrineTW
Accepted
測資全過
輸出正確
你太電了
我也是沒辦法
Wrong Answer
有一筆或多筆測資沒過
未考慮極端測資
扣沒照著你理想跑
Time Limit Error
你扣跑太慢了
超出規定時間
時間複雜度過高
無限迴圈沒有 break
Memory Limit Error
記憶體被你
用爆了啦
請不要開超大陣列好嗎
遞迴打太深ㄌ
Runtime Error
扣跑到一半吃
segfault
陣列的索引值超出範圍
訪問 deleted pointer
Complie Error
在編譯過程中
就已經爛掉了
你直接在解答區打扣 ==
拿非常數去設定 array
ios_base::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
// 其實第三行可以不加,但我習慣會給
直接用 '\n' 換行就好ㄌ
cin.tie(0);
ios_base::sync_with_stdio(false);
BrineTW 有講過這個,但他只有提到用 auto 遍歷
vector <int> vec; // type:vector
auto tmp = vec; // type:vector
vector <vector <pair <pair <int, char>, pair <int, bool> > > > mess;
auto tmp = mess[0][0].first; // type:pair <int, char>
vector <int> vec(100, 1);
for (int i = 0; i < vec.size(); i++)
cout << vec[i] << ' ';
for (auto to:vec) // type of auto:int
cout << to << ' ';
其實 vector 下節課會重教,他本來就是那邊的範圍
int input_count; cin >> input_count;
while (input_count--){
}
void typeCheck (int tmp){
cout << "This is an integer\n";
}
void tyoeCheck (bool tmp){
cout << "This is a boolean\n";
}
int main (){
cout << typeCheck(1) << '\n';
//這絕對會判成整數
}
#define ll long long int
#define vveci vector <vector <int> >
#define gura ios_base::sync_with_stdio(false), cin.tie(0), cout.tie(0);
語法
#define (空格) [關鍵字] (空格) [內容]
換言之,系統會在你 code 運行前,就先把關鍵字 全部取代完!
* 預處理:在實際執行你的 code 之前,就會先做完的事情
除非你想把 ; 也 define 進去
#define sum(a, b) a + b
#define mod(a, b) (a+b) % b
#define print(str) cout << str << '\n';
現在你們可以拿 C++ 寫 python 了
int main (){
cout << sum(2, 3) << '\n';
cout << sum(-2, 3) << '\n';
}
output:
5
1
int main (){
print("This is C++")
}
output:
This is C++
關鍵字 | 內容 | 備註 |
---|---|---|
veci | vector <int> | 好像不怎麼常見 |
pii | pair <int, int> | 你們還沒教,但很常見 |
F / FF | first | 你們還沒教,但很常見 |
S / SS | second | 你們還沒教,但很常見 |
pq | priority_queue | 你們還沒教,但超常見 |
ll | long long int | 常見到幾乎等於默認 |
usll | unsigned long long int | 默認關鍵字不是這個 |
(3 行 i/o 優化) | ||
debug(x) | cerr << x << '\n | Aaw 大推 |
我自己常用的一些 define
(有些很毒不建議模仿)
語法
printf(" [輸出文字與格式指定碼] ", [要填入的變數] );
printf("print without cout\n");
print without cout
int a = 2, b = 5;
printf("%d + %d = %d\n", a, b, a+b);
註:整數的格式指定碼為 %d
2 + 5 = 7
%d | 10 進位整數 |
---|---|
%f | 浮點數 |
%s | 字串 |
%p | 指標記憶體 |
最常用的格式指定碼
1. 亂砸測資
看清楚題目的範圍,常常就是被那一筆極限搞到
可能會出現
N = 0 且 M = 0 的 case
M 是小數的 case 也合理
相信我,80 % 都不是照著你預想的跑,人難免會失誤
2. cout 大法
把每個執行階段的關鍵變數都 cout 出來
3. 註解大法
把一大部分的程式註解掉,每次只讓小部份執行
尋找問題出在哪裡,一旦哪行進來程式就無法執行
(常用於 RE、MLE、CE 時)
4. ㄟ學長姐幫我看一下啦拜託嘛我超級需要你耶
學長姊都很 nice,絕對會幫忙你的
但一般我們也只是重複上面三個步驟
只是經驗稍微多一點而已
不要覺得這些很基本耶
因為有很多一三都不會,吐血
在學演算法前的必備技能
遞迴ㄛ,在 function 中呼喚自己, 真的就這樣而已啊
– 阿蘇自己亂掰的
"若有一函數,是由自身所定義的,我們將此稱為遞迴"
我想知道費氏數列的第 N 項
你有辦法通靈出一段 code
負責解決這個問題ㄇ
數學上的遞迴
e.g. 費波那契數列
* 如果所求項是第 0 項(雖然理論上不可能)
或第 1 項,要把它特判掉
否則會出現 Fibonacci (-1) 這種奇怪東東
當然你也可以拿迴圈,就本題來說速度甚至會更快
但暫不列入今日討論範圍
int Fibonacci(int tar, int index = 2, int a = 1, int b = 0){
if (tar == 0) return 0;
// 如果是第 0 項,定義為 0
if (tar == 1) return 1;
// 如果是第 1 項,定義為 1
if (tar == index) return a + b;
// 若已經找到所求項,則 a + b 就是答案
return Fibonacci(tar, ++index, a+b, a);
// 否則更新 a、b,繼續找下一項
}
拿遞迴刻,這堂課的重點
終端條件:讓你遞迴能結束的方法
int Fibonacci(int tar, int index = 2, int a = 1, int b = 0){
if (tar == 0) return 0;
if (tar == 1) return 1;
if (tar == index) return a + b;
return Fibonacci(tar, ++index, a+b, a);
}
第 4 行的 if
當找到我們要的那項
就把遞迴結束
int Fibonacci(int tar, int index = 2, int a = 1, int b = 0){
if (tar == 0) return 0;
if (tar == 1) return 1;
return Fibonacci(tar, ++index, a+b, a);
if (tar == index) return a + b;
}
左邊就是無窮迴圈
條件寫心酸的嗎.jpg
int F (int x){
if (x == 1) return 1;
if (x%2 == 0) return F(x/2);
return F(x-1) + F(x+1);
}
或跟 1 做 and 等於 0
沒難度吧。那我們幫題目穿上衣服(X
我本來以為要自己推導函式,怕爆
vector <int> ret(1e5+17, 0);
int query (int num, int count){
if (count > 0) return query(ret[num], --count);
return num;
}
往後很多題目,你第一個想到的往往是遞迴解
跟迴圈寫法相較,遞迴會比較好懂
等他一來一往的時間,常常已經 TLE 了
MAXN 大的題目,會因為開太深而直接爆記憶體
總結:遞迴是種語法兼概念,主要是應用在 演算法實作 上,
但單獨考遞迴的題目其實不多,常常是暴力解擔當
不過有個破考試對遞迴情有獨鐘ㄛ...
long long f(int x, int y) {
if (x == 0) return y;
else return f(x - 1, x + y);
}
int main() {
printf("%d\n", f(100, 10));
//cout << f(100, 10) << '\n';
}
APCS 觀念題 111 年 01 月 第一場 p13
求輸出,沒有選項因為沒人記得。
k(30)
k(7)
k(1)
k(0)
int k(int n) {
switch(n) {
case 0: return n;
case 1: return n + k(n / 2);
default: return n + k(n / 4);
}
return n;
}
int main() {
printf("%d\n", k(30));
//cout << k(30) << '\n';
}
APCS 觀念題 111 年 01 月 第一場 p14
求輸出,沒有選項因為沒人記得。
void func(int n) {
printf("%d ", 2 * n);
//cout << 2*n << '\n';
if (n < 4)
func(n + 1);
printf("%d ", 2 * n - 1);
//cout << 2*n - 1 << '\n';
}
int main (){
func(2);
}
APCS 觀念題 111 年 01 月 第二場 p2
求輸出,沒有選項因為沒人記得。
[ output ]
L5
L5
L6
L6
L6
k(2)
L2
k(3)
L2
k(4)
L2
其實就,輾轉相除法,一模一樣
很煩,超級煩,強烈建議用 vector 做
如果你做出了上一題,那這就是水題
已經有點實作題的味道ㄌ
八皇后問題,去年有被改編成社賽題目
做為防破台門檻。你們肯定可以的吧:)
本來要當成例題,但我沒時間做
當時被我跟先帝公認的煩死人題目
特別有趣,感受痛苦吧:)
有個內建函式可以解決這個
但試試用遞迴寫 la
如果現在想不出來,可以等上完 DP 再回頭
相對來說比較簡單
前面的例題,且與 TCIRC_b001 完全等價
呈 CSES_2165,不過又多了變化
現在開始與結束狀態都不固定了
先思考一下他的遞迴式為何?
題目本身是不難
但輸出格式很麻煩
aka 變數縫合怪
若我們今天要儲存 N 個四維坐標 {x, y, z, w}
你直覺想到的做法會是什麼?
const int MAXN = 1007;
int x[MAXN], y[MAXN], z[MAXN], w[MAXN];
int main (){
int N; cin >> N;
for (int i = 1; i <= N; i++) cin >> x[i] >> y[i] >> z[i] >> w[i];
}
#define veci vector <int>
veci x(1007), y(1007), z(1007), w(1007);
int main (){
int N; cin >> N;
for (int i = 1; i <= N; i++) cin >> x[i] >> y[i] >> z[i] >> w[i];
}
明明是同一組數據,卻要翻四個不同地方,感覺 缺乏整體性
語法(定義一個 struct)
sturct [結構名稱]{ //沒有 (),否則你會宣告成函式
[成員變數或成員函式]
}; //這個分號一定要加!!!
struct location {
int x, y, z, w;
};
例如剛剛的四維坐標
這樣存會好很多
struct wordle {
int problem_ID;
string answer;
char letter[6];
bool correct;
};
當然,你也可以
儲存類型不同的變數
語法(宣告一個 struct)
[結構名稱] [命名];
location dot;
const int MAXN = 1007;
location dot_set [MAXN];
vector <location> dot_set_2(MAXN);
while (int i = 1; i <= N; i++)
cin >> dot_set[i].x >> dot_set[i].y >> dot_set[i].z >> dot_set[i].w;
語法(汲取成員變數)
[結構變數].[成員變數]
struct location {
int x = 0, y = 0, z = 0, w = 0;
};
int main (){
location dot;
dot.x = 5; dot.w = 37;
printf("x = %d, y = %d, z = %d, w = %d\n", dot.x, dot.y, dot.z, dot.w);
}
output:x = 5, y = 0, z = 0, w = 37
struct location {
int x = 0, y = 0, z = 0, w = 0;
void printLoc (){
printf("x = %d, y = %d, z = %d, w = %d\n", x, y, z, w);
}
};
int main (){
location dot, dot_2; //dot_2 就是初始化的狀態
dot.x = 1; dot.y = 2; dot.z = 3; dot.w = 4;
dot.printLoc();
dot_2.printLoc();
}
把整個函式塞進 struct 的例子:
output:x = 1, y = 2, z = 3, w = 4
x = 0, y = 0, z = 0, w = 0
除了 print()、girls_average()、boys_average() 外,
不要上傳任何其他東西,也不要 #include 或 #define
同時它沒有 using namespace std,所以 cout 前要加 std::
拜託,真的,相信我,用 struct 做
否則你會極度痛苦
它有點煩,有問題可以來問我沒關係
建議用遞迴先做個 gcd() 出來
在這題會很有幫助
直接針對記憶體操作
0x00 | 0x01 | 0x02 | 0x03 | 0x04 | 0x05 |
---|---|---|---|---|---|
0x06 | 0x07 | 0x08 | 0x09 | 0x0a | 0x0b |
int i = 0;
i
0
int j = 2;
j
2
事實上,記憶體編號不會那麼漂亮,而且 int 佔 32 bits。但我暫時不鳥它 :partying_face:
i++;
j = i;
1
0x00 | 0x01 | 0x02 | 0x03 | 0x04 | 0x05 |
---|---|---|---|---|---|
0x06 | 0x07 | 0x08 | 0x09 | 0x0a | 0x0b |
i
1
j
2
變數名稱只是方便我們打 code
電腦還是以記憶體為主
-類別:同 所指變數 的類別
-名字:就,這個指標的命名
-值:即 所指變數 的位置
-位置:這個指標本體的所在,如前文所提,佔 8 bytes
"指標就像一隻寄生蟲,會指向一個變數,寄生在它身上。 擁有該變數的所有特性,共享一副身體。
任何一方受到傷害,都會連累另一個人"
- 對,阿蘇剛剛唬爛出來的
> 宣告一個指標叫 num_ptr,值設為 num 的位置!
語法
[指標的類別] *[指標命名] = &[指向的變數名字]
int num = 5;
int *num_ptr = #
int num = 5;
int *num_ptr = #
語法
*[指標名稱]
cout << *num_ptr << '\n'; //5
num += 5;
*num_ptr += 5
or
cout << *num_ptr << ' ' << num << '\n'; // 10 10
Cuz 指標是直接 指向位置
也是直接更改 所指位置 的數字
int arr[10];
0x08 | 0x09 | 0x0a | 0x0b | 0x0c |
---|---|---|---|---|
0x0d | 0x0e | 0x0f | 0x10 | 0x11 |
arr[0]
arr[1]
arr[2]
arr[3]
arr[4]
arr[5]
arr[6]
arr[7]
arr[8]
arr[9]
* 為求方便,所有記憶體位置都是假設ㄉ,且令 int 只佔 1 bit
int arr[10] = {};
int *arr_ptr = &arr[0];
cout << arr << ' ' << arr_ptr << '\n';
// 別忘了指標本身的值是記憶體位置ㄛ,我並沒有加 *
output :
0x08 0x08
int arr[10] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
printf("%p %p %p\n", arr, arr+1, arr+2); // %p 就是直接印出指標的值
printf("%d %d %d\n", *arr, *arr+1, *arr+2);
0x08 | 0x09 | 0x0a | 0x0b | 0x0c |
---|---|---|---|---|
0x0d | 0x0e | 0x0f | 0x10 | 0x11 |
arr[0]
arr[1]
arr[2]
arr[3]
arr[4]
arr[5]
arr[6]
arr[7]
arr[8]
arr[9]
* 事實上,每次平移應該會跳一個 int 的大小,也就是 32 格,aka 4 bytes
output :
0x08 0x09 0x10
1 2 3
int arr[10] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
int err[10] = arr;
這樣會 Complie Error
int arr[10] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
int *err = arr;
正確做法
"所以 Pointer 實際上有什麼用?
剛剛的狀況看起來都沒什麼價值啊"
喔對啊,剛剛的狀況的確不太會用到
至少我到現在
還沒遇過半題,會需要開指向一般變數的 pointer
指標的概念很重要
等上面那些酷東西進來後,你會很感謝自己的
就算現階段比較難應用
請看清楚提敘,乖乖照著規定做
否則根本過不了 (。・∀・)ノ゙
跟上一題超級像
我甚至不記得我當年在資芽
有寫過這一題
所以顯然很水
永久更改 function 中的傳入值
宣告方法
[變數型態] &[參考命名] = [宿主名稱]
int b = 5; //令其儲存在 0xff
int &a = b; // a 的型態是 int&,aka 參考,寄生在 b 上面
cout << a; //輸出 a 的值
cout << &a; //對 a 取址
output:5
output:0xff
此時 a、b 完全等價!
& | * | |
---|---|---|
用於宣告 | 這是一個 參考 | 這是一個 指標 |
其他時候 | 取址符號 | 取值符號 |
[ 用法比較,不要搞混否則會出事 ]
Pointer v.s Reference
int a = 0;
int b = 5;
infor | ||||
---|---|---|---|---|
pos | 0x01 | 0x02 |
a
b
0
5
c
0x01
0x03
int *c = &a;
int &d = b;
d
cout << c << '\n'; //0x01
cout << &c << '\n'; //0x03
cout << d << '\n'; //5
cout << &d << '\n'; //0x02
cout << *c << '\n'; //0
cout << *d << '\n'; //Compile Error
L1:* 是宣告指標,& 是取址
L2:& 是宣告參考
全部的 & 都是取址
L1:* 是取所指變數的值
L2:* 會讓你扣爛掉
變數生命週期
btw,在全域 array 的上限會提高一些
int num = 0; //全域變數,作用範圍是整份 code
int main (){
int num = 2; //區域變數,作用範圍是 main() 裡面
cout << num; // 2
}
Bruh,我才不要用此類文謅謅的詞來解釋這種簡單語法
void PlusOne (int num){
num++;
cout << "In function, n = " << num << '\n';
}
int main (){
int n = 0;
cout << "Before function, num = " << n << '\n';
PlusOne(n);
cout << "After function, n = " << n << '\n';
}
output:
Before function, n = 0
In function, num = 1
After function, n = 0
廢話,因為 num 的作用範圍 只在 PlusOne 裡面 啊
void PlusOne (int &num){
num++;
cout << "In function, n = " << num << '\n';
}
int main (){
int n = 0;
cout << "Before function, num = " << n << '\n';
PlusOne(n);
cout << "After function, n = " << n << '\n';
}
output:
Before function, n = 0
In function, num = 1
After function, n = 1
我只有動第 1 行
還是我現場再寫一份出來
void reverse (vector <int> &vec){
vector <int> tmp = vec;
for (int i = 0; i < vec.size(); i++) vec[i] = tmp[vec.size()-1-i];
}
void reverse (int arr[], int length){ //你現在知道 array 是指標ㄌ
*tmp = arr;
for (int i = 0; i < length; i++) arr[i] = tmp[length-1-i];
}
現在我來 鞭屍 了,因為有人 簡報寫錯 ╮(╯_╰)╭
int h (int y[][11])
int h (int (*y)[11])
這樣就夠了
我再多講下去
Ivan Lo 又要說我在噁別人
因為原本那家倒了,有夠瞎
總務要哭出來了
真的不考慮現在交嗎
我要給 ㄘㄇㄏ 一個
屬於他的版面