迴圈
LOOOOOP!
臣亮言 @ NTU CSIE
March 8, 2025
Sprout 資訊之芽北區 C 班
在開始之前......
LOOP 迴圈
Intro
while 迴圈
for 迴圈
流程控制
進階用法
補充資料
Intro
Intro
已經會的先忘掉 想想利用前幾堂的內容怎做到以下的任務
cout << 1 << '\n';
輸出 1
輸出 1 ~ 5
cout << 1 << '\n';
cout << 2 << '\n';
cout << 3 << '\n';
cout << 4 << '\n';
cout << 5 << '\n';
輸出 1 ~ 55688
我不會,長大後再學習
有時候我們會希望程式重複且規律地執行一連串指令
已經會的先忘掉 想想利用前幾堂的內容怎做到以下的任務
cout << 1 << '\n';
輸出 1
輸出 1 ~ 5
cout << 1 << '\n';
cout << 2 << '\n';
cout << 3 << '\n';
cout << 4 << '\n';
cout << 5 << '\n';
輸出 1 ~ 55688
我不會,長大後再學習
余憶童稚時...
迴圈的目的
Intro
while 迴圈
while 迴圈〉
1
語法 & 執行架構
while(/*condition*/){
/*process...*/
}
True
False
Condition
Process
進入迴圈時判斷 (條件) 是否成立
若成立則執行 {程序}
執行完回頭檢查 (條件)
仍成立則持續反覆執行
否則離開迴圈
2
逐步執行
1
int a = 3;
while(a <= 100){
cout << a << ' ';
a *= 3;
}
cout << "\nstop at a = " << a << '\n';
a = 3
Output
3 9 27 81 stop at a = 243
9
27
81
243
語法 & 執行架構
1
語法 & 執行架構
2
逐步執行
True
False
a <= 100 ?
print a
a *= 3
a = 3
print stop point a
輸出 100 以下 3 的倍數
並在下一行輸出第一個超過 100 的 3 的倍數
while 迴圈〉
進入迴圈時判斷 (條件) 是否成立
若成立則執行 {程序}
執行完回頭檢查 (條件)
仍成立則持續反覆執行
否則離開迴圈
while(/*condition*/){
/*process...*/
}
3
do-while
1
a = 3
Output
3 9 27 81 stop at a = 243
9
27
81
243
語法 & 執行架構
1
語法 & 執行架構
2
逐步執行
True
False
a <= 100 ?
print a
a *= 3
a = 3
print stop point a
輸出 100 以下 3 的倍數
並在下一行輸出第一個超過 100 的 3 的倍數
while 迴圈〉
int a = 3;
while(a <= 100){
cout << a << ' ';
a *= 3;
}
cout << "\nstop at a = " << a << '\n';
3
do-while
在 C/C++ 中,除了 while 迴圈以外,還有一個結構的十分類似的 do-while 迴圈
do{
/*process*/
}while(/*condition*/);
//要加分號!
從邏輯上來觀察
do-while 迴圈並不是在開頭判斷條件
而是在執行完迴圈的時候才會判斷
1
Output
3 9 27 81 stop at a = 243
9
27
81
243
語法 & 執行架構
1
語法 & 執行架構
2
逐步執行
True
False
a <= 100 ?
print a
a *= 3
a = 3
print stop point a
輸出 100 以下 3 的倍數
並在下一行輸出第一個超過 100 的 3 的倍數
while 迴圈〉
3
do-while
在 C/C++ 中,除了 while 迴圈以外,還有一個結構的十分類似的 do-while 迴圈
do{
/*process*/
}while(/*condition*/);
從邏輯上來觀察
do-while 迴圈並不是在開頭判斷條件
而是在執行完迴圈的時候才會判斷
while(/*condition*/){
/*process*/
}
True
False
Condition
Process
True
False
Condition
Process
多比叫一下
1
Output
3 9 27 81 stop at a = 243
9
27
81
243
語法 & 執行架構
1
語法 & 執行架構
2
逐步執行
True
False
a <= 100 ?
print a
a *= 3
a = 3
print stop point a
輸出 100 以下 3 的倍數
並在下一行輸出第一個超過 100 的 3 的倍數
while 迴圈〉
3
do-while
在 C/C++ 中,除了 while 迴圈以外,還有一個結構的十分類似的 do-while 迴圈
do{
/*process*/
}while(/*condition*/);
從邏輯上來觀察
do-while 迴圈並不是在開頭判斷條件
而是在執行完迴圈的時候才會判斷
/*process*/
while(/*condition*/){
/*process*/
}
True
False
Condition
Process
True
False
Condition
Process
多比叫一下
事實上,更常見的說法是
do-while 是忽略起始條件,強制執行第一次迴圈的 while
$$\iff$$
:好像差不多,那這個語法的目的是什麼?
:有沒有更實際的例子?
對,就是省去那一次重複的 process
還真的沒有有些時候我們條件會用到需要執行第一次迴圈才能獲取的資料
1
Output
3 9 27 81 stop at a = 243
9
27
81
243
語法 & 執行架構
1
語法 & 執行架構
2
逐步執行
True
False
a <= 100 ?
print a
a *= 3
a = 3
print stop point a
輸出 100 以下 3 的倍數
並在下一行輸出第一個超過 100 的 3 的倍數
for 迴圈
3
do-while
在 C/C++ 中,除了 while 迴圈以外,還有一個結構的十分類似的 do-while 迴圈
從邏輯上來觀察
do-while 迴圈並不是在開頭判斷條件
而是在執行完迴圈的時候才會判斷
True
False
Condition
Process
True
False
Condition
Process
多比叫一下
do{
/*process*/
}while(/*condition*/);
/*process*/
while(/*condition*/){
/*process*/
}
事實上,更常見的說法是
do-while 是忽略起始條件,強制執行第一次迴圈的 while
$$\iff$$
:好像差不多,那這個語法的目的是什麼?
:有沒有更實際的例子?
對,就是省去那一次重複的 process
還真的沒有有些時候我們條件會用到需要執行第一次迴圈才能獲取的資料
for 迴圈
for 迴圈〉
1
語法 & 執行架構
for(initialize; condition; iterate){
/*process...*/
}
進入迴圈時先進行初始化
判斷條件成立後進入迴圈
迴圈結束後
先執行迭代動作再判斷條件
成立則反覆如此
否則離開迴圈
initialize:初始化
condition:條件
iterate:迭代
True
False
Process
Iterate
Condition
Initialize
※三個皆可省略,但要保留 ';'
注意省略條件會變無窮迴圈
2
逐步執行
for 迴圈〉
1
語法 & 執行架構
int sum = 0;
for(int i = 1; i <= 5; i++){
sum += i;
}
cout << "sum of N <= 5 = ";
cout << sum << '\n';
initialize:初始化
condition:條件
iterate:迭代
True
False
sum += i
i++
i <= 5 ?
i = 1
0
1
2
逐步執行
1
2
3
3
6
4
10
5
15
6
sum =
i =
3
還是有點複雜,能不能用 while 寫?
Output
sum of N <= 5 = 15
for vs. while
進入迴圈時先進行初始化
判斷條件成立後進入迴圈
迴圈結束後
先執行迭代動作再判斷條件
成立則反覆如此
否則離開迴圈
※三個皆可省略,但要保留 ';'
注意省略條件會變無窮迴圈
for 迴圈〉
1
語法 & 執行架構
int sum = 0;
for(int i = 1; i <= 5; i++){
sum += i;
}
cout << "sum of N <= 5 = ";
cout << sum << '\n';
2
逐步執行
3
for vs. while
int sum = 0, i = 1;
while(i <= 5){
sum += i;
i++;
}
cout << "sum of N <= 5 = ";
cout << sum << '\n';
for loop
while loop
把初始化放到迴圈前
再把迭代動作放到迴圈尾
就能用 while 模擬 for 的行為
※其實生命週期有差,但之後才會教 (X
既然可以用 while 取代 for
為什麼要設計這個語法?
適合處理迭代具規律性的問題(計數器)
通常容易推斷執行次數
e.g. 數列級數
適合處理有特定終止條件
不好直覺推斷執行次數的問題
e.g. 數列找值
True
False
sum += i
i++
i <= 5 ?
i = 1
0
1
1
2
3
3
6
4
10
5
15
6
sum =
i =
還是有點複雜,能不能用 while 寫?
Output
sum of N <= 5 = 15
for 迴圈〉
1
語法 & 執行架構
int sum = 0;
for(int i = 1; i <= 5; i++){
sum += i;
}
cout << "sum of N <= 5 = ";
cout << sum << '\n';
2
逐步執行
3
for vs. while
這個例子實際上是 for 最典型的寫法
透過設定計數器與終止位置
來達成如從 1 ~ 5、0 ~ 9、甚至 9 ~ 0 的效果
比起 while 來得更簡潔有力
(這個班 95% 的時機都不會脫離這類用途)
當然也能夠利用 for 迴圈的設計來達成「特殊」的操作 像是迭代式線段樹 int_to_string
既然可以用 while 取代 for
為什麼要設計這個語法?
適合處理迭代具規律性的問題(計數器)
通常容易推斷執行次數
e.g. 數列級數
適合處理有特定終止條件
不好直覺推斷執行次數的問題
e.g. 數列找值
流程控制
1
語法 & 執行架構
2
逐步執行
3
for vs. while
int sum = 0;
for(int i = 1; i <= 5; i++){
sum += i;
}
cout << "sum of N <= 5 = ";
cout << sum << '\n';
這個例子實際上是 for 最典型的寫法
透過設定計數器與終止位置
來達成如從 1 ~ 5、0 ~ 9、甚至 9 ~ 0 的效果
比起 while 來得更簡潔有力
(這個班 95% 的時機都不會脫離這類用途)
當然也能夠利用 for 迴圈的設計來達成「特殊」的操作 像是迭代式線段樹 int_to_string
流程控制
流程控制〉
1
語法 & 執行架構
在迴圈中我們還有兩種語法可以進行特殊的流程控制
while(condition){
//...
continue;
//...
}
continue
※這裡用 while 舉例
實際上所有迴圈都能用
跳過後續的程式
回到迴圈頭(1)執行下一次迴圈
condition
process
continue
流程控制〉
1
語法 & 執行架構
在迴圈中我們還有兩種語法可以進行特殊的流程控制
while(condition){
//...
break;
//...
}
※這裡用 while 舉例
實際上所有迴圈都能用
break
跳出整個迴圈到迴圈外(6)
(迴圈內後續程式也不會執行)
condition
process
break
2
CHALLENGE
流程控制〉
1
語法 & 執行架構
有一個數字獸很喜歡吃數字
但他不喜歡吃偶數,只要遇到偶數他就不會吃
不過他偶數中他最討厭 1024,只要遇到他會立刻翻桌走人
請問他吃下去的數字總和是?
int sum = 0;
int food;
while(true){
cin >> food;
if(food % 2 == 0) continue;
if(food == 1024) break;
sum += food;
}
cout << sum << '\n';
int sum = 0;
int food;
while(true){
cin >> food;
if(food == 1024) break;
if(food % 2 == 0) continue;
sum += food;
}
cout << sum << '\n';
2
CHALLENGE
在迴圈中我們還有兩種語法可以進行特殊的流程控制
while(condition){
//...
break;
//...
}
break
跳出整個迴圈到迴圈外(6)
(迴圈內後續程式也不會執行)
condition
process
break
流程控制〉
int sum = 0;
int food;
while(true){
cin >> food;
if(food % 2 == 0) continue;
if(food == 1024) break;
sum += food;
}
cout << sum << '\n';
int sum = 0;
int food;
while(true){
cin >> food;
if(food == 1024) break;
if(food % 2 == 0) continue;
sum += food;
}
cout << sum << '\n';
注意 1024 也是偶數
所以左邊遇到 1024 會先被 continue
於是不會執行到後續判斷是否為 1024 的部分
因此右邊才是正確的寫法
2
CHALLENGE
有一個數字獸很喜歡吃數字
但他不喜歡吃偶數,只要遇到偶數他就不會吃
不過他偶數中他最討厭 1024,只要遇到他會立刻翻桌走人
請問他吃下去的數字總和是?
進階用法
1
語法 & 執行架構
2
逐步執行
3
int sum = 0;
int food;
while(true){
cin >> food;
if(food % 2 == 0) continue;
if(food == 1024) break;
sum += food;
}
cout << sum << '\n';
int sum = 0;
int food;
while(true){
cin >> food;
if(food == 1024) break;
if(food % 2 == 0) continue;
sum += food;
}
cout << sum << '\n';
注意 1024 也是偶數
所以左邊遇到 1024 會先被 continue
於是不會執行到後續判斷是否為 1024 的部分
因此右邊才是正確的寫法
CHALLENGE
進階用法
進階用法〉
1
輸入直到EOF?
在某些古老的題目中,出題者可能會很壞的不告訴你總共會有多少次輸入
這時題目通常附註輸入直到 EOF(End-Of-File)
EOF 如其名,標示一個檔案的結束
在 C++ 中,cin 如果讀取到 EOF 便會回傳 false
因此可以直接在條件式寫 cin 來達成(這邊不用加分號)
while(cin >> ...){
//...
}
※本地端測試時可以用 ctrl + Z (Win) 輸入EOF
ctrl + D (Linux/MacOS)
2
巢狀迴圈
進階用法〉
1
輸入直到EOF?
2
巢狀迴圈
簡單來說:印出一張九九乘法表
但是剛剛只教到一個變量的迴圈,現在有兩個變量
回想一下背九九乘法表的方式
1 × 1 = 1,1 × 2 = 2,...,1 × 9 = 9
2 × 1 = 2,2 × 2 = 4,...,2 × 9 = 18
...
9 × 1 = 9,9 × 2 = 18,...,9 × 9 = 81
在某些古老的題目中,出題者可能會很壞的不告訴你總共會有多少次輸入
這時題目通常附註輸入直到 EOF(End-Of-File)
EOF 如其名,標示一個檔案的結束
在 C++ 中,cin 如果讀取到 EOF 便會回傳 false
因此可以直接在條件式寫 cin 來達成(這邊不用加分號)
while(cin >> ...){
//...
}
※本地端測試時可以用 ctrl + Z (Win) 輸入EOF
ctrl + D (Linux/MacOS)
進階用法〉
2
巢狀迴圈
1 × 1 = 1,1 × 2 = 2,...,1 × 9 = 9
2 × 1 = 2,2 × 2 = 4,...,2 × 9 = 18
...
9 × 1 = 9,9 × 2 = 18,...,9 × 9 = 81
乘數從 1 ~ 9,完成一輪後被乘數加一
1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | |
1 | |||||||||
2 | |||||||||
3 | |||||||||
4 | |||||||||
5 | |||||||||
6 | |||||||||
7 | |||||||||
8 | |||||||||
9 |
+1
+1
+1
簡單來說:印出一張九九乘法表
但是剛剛只教到一個變量的迴圈,現在有兩個變量
回想一下背九九乘法表的方式
進階用法〉
2
巢狀迴圈
1 × 1 = 1,1 × 2 = 2,...,1 × 9 = 9
2 × 1 = 2,2 × 2 = 4,...,2 × 9 = 18
...
9 × 1 = 9,9 × 2 = 18,...,9 × 9 = 81
乘數從 1 ~ 9,完成一輪後被乘數加一
1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | |
1 | |||||||||
2 | |||||||||
3 | |||||||||
4 | |||||||||
5 | |||||||||
6 | |||||||||
7 | |||||||||
8 | |||||||||
9 |
+1
+1
+1
進階用法〉
2
巢狀迴圈
1 × 1 = 1,1 × 2 = 2,...,1 × 9 = 9
2 × 1 = 2,2 × 2 = 4,...,2 × 9 = 18
...
9 × 1 = 9,9 × 2 = 18,...,9 × 9 = 81
乘數從 1 ~ 9,完成一輪後被乘數加一
1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | |
1 | |||||||||
2 | |||||||||
3 | |||||||||
4 | |||||||||
5 | |||||||||
6 | |||||||||
7 | |||||||||
8 | |||||||||
9 |
+1
+1
+1
進階用法〉
2
巢狀迴圈
1 × 1 = 1,1 × 2 = 2,...,1 × 9 = 9
2 × 1 = 2,2 × 2 = 4,...,2 × 9 = 18
...
9 × 1 = 9,9 × 2 = 18,...,9 × 9 = 81
乘數從 1 ~ 9,完成一輪後被乘數加一
1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | |
1 | |||||||||
2 | |||||||||
3 | |||||||||
4 | |||||||||
5 | |||||||||
6 | |||||||||
7 | |||||||||
8 | |||||||||
9 |
+1
+1
+1
進階用法〉
2
巢狀迴圈
1 × 1 = 1,1 × 2 = 2,...,1 × 9 = 9
2 × 1 = 2,2 × 2 = 4,...,2 × 9 = 18
...
9 × 1 = 9,9 × 2 = 18,...,9 × 9 = 81
乘數從 1 ~ 9,完成一輪後被乘數加一
1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | |
1 | |||||||||
2 | |||||||||
3 | |||||||||
4 | |||||||||
5 | |||||||||
6 | |||||||||
7 | |||||||||
8 | |||||||||
9 |
+1
+1
+1
進階用法〉
2
巢狀迴圈
1 × 1 = 1,1 × 2 = 2,...,1 × 9 = 9
2 × 1 = 2,2 × 2 = 4,...,2 × 9 = 18
...
9 × 1 = 9,9 × 2 = 18,...,9 × 9 = 81
乘數從 1 ~ 9,完成一輪後被乘數加一
1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | |
1 | |||||||||
2 | |||||||||
3 | |||||||||
4 | |||||||||
5 | |||||||||
6 | |||||||||
7 | |||||||||
8 | |||||||||
9 |
+1
+1
+1
進階用法〉
2
巢狀迴圈
1 × 1 = 1,1 × 2 = 2,...,1 × 9 = 9
2 × 1 = 2,2 × 2 = 4,...,2 × 9 = 18
...
9 × 1 = 9,9 × 2 = 18,...,9 × 9 = 81
乘數從 1 ~ 9,完成一輪後被乘數加一
1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | |
1 | |||||||||
2 | |||||||||
3 | |||||||||
4 | |||||||||
5 | |||||||||
6 | |||||||||
7 | |||||||||
8 | |||||||||
9 |
+1
+1
+1
進階用法〉
2
巢狀迴圈
1 × 1 = 1,1 × 2 = 2,...,1 × 9 = 9
2 × 1 = 2,2 × 2 = 4,...,2 × 9 = 18
...
9 × 1 = 9,9 × 2 = 18,...,9 × 9 = 81
乘數從 1 ~ 9,完成一輪後被乘數加一
1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | |
1 | |||||||||
2 | |||||||||
3 | |||||||||
4 | |||||||||
5 | |||||||||
6 | |||||||||
7 | |||||||||
8 | |||||||||
9 |
+1
+1
+1
進階用法〉
2
巢狀迴圈
1 × 1 = 1,1 × 2 = 2,...,1 × 9 = 9
2 × 1 = 2,2 × 2 = 4,...,2 × 9 = 18
...
9 × 1 = 9,9 × 2 = 18,...,9 × 9 = 81
乘數從 1 ~ 9,完成一輪後被乘數加一
1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | |
1 | |||||||||
2 | |||||||||
3 | |||||||||
4 | |||||||||
5 | |||||||||
6 | |||||||||
7 | |||||||||
8 | |||||||||
9 |
+1
+1
+1
進階用法〉
2
巢狀迴圈
1 × 1 = 1,1 × 2 = 2,...,1 × 9 = 9
2 × 1 = 2,2 × 2 = 4,...,2 × 9 = 18
...
9 × 1 = 9,9 × 2 = 18,...,9 × 9 = 81
乘數從 1 ~ 9,完成一輪後被乘數加一
1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | |
1 | |||||||||
2 | |||||||||
3 | |||||||||
4 | |||||||||
5 | |||||||||
6 | |||||||||
7 | |||||||||
8 | |||||||||
9 |
+1
+1
+1
可以觀察到每次乘數的迭代都依循著被乘數的迭代
進階用法〉
2
巢狀迴圈
1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | |
1 | |||||||||
2 | |||||||||
3 | |||||||||
4 | |||||||||
5 | |||||||||
6 | |||||||||
7 | |||||||||
8 | |||||||||
9 |
可以觀察到每次乘數的迭代都依循著被乘數的迭代
更視覺化的想法,淺色箭頭附屬在深色箭頭上
一個箭頭就是一個迴圈
大迴圈包著小迴圈 ⇒ 巢狀迴圈
#include <bits/stdc++.h>
using namespace std;
int main(){
for(int i = 1; i <= 9; i++)
for(int j = 1; j <= 9; j++)
cout << i << " x " << j << " = " << i * j << '\n';
}
1 × 1 = 1,1 × 2 = 2,...,1 × 9 = 9
2 × 1 = 2,2 × 2 = 4,...,2 × 9 = 18
...
9 × 1 = 9,9 × 2 = 18,...,9 × 9 = 81
乘數從 1 ~ 9,完成一輪後被乘數加一
+1
+1
+1
進階用法〉
2
巢狀迴圈
Flag
3
尤其在寫 for 迴圈時,我們有一些約定俗成的習慣
對於用來迭代的計數器,我們習慣用 i 、 j 命名
而對於執行的總次數,我們習慣用 n 、 m 來命名
當然在不同情況下也有特殊的命名法則
像是具有座標類意義,可以用 x 、 y 、 z
對於表格,可以用 col 、 row
這在陣列的單元極為重要,請養成良好的命名習慣
for(int i = 0; i < n; i++){
for(int j = 0; j < m; j++){
//...
}
}
另外對於執行 n 次
我們習慣用從 0 ~ n - 1 的方式迭代
下禮拜會細講這背後的原理
1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | |
1 | |||||||||
2 | |||||||||
3 | |||||||||
4 | |||||||||
5 | |||||||||
6 | |||||||||
7 | |||||||||
8 | |||||||||
9 |
可以觀察到每次乘數的迭代都依循著被乘數的迭代
更視覺化的想法,淺色箭頭附屬在深色箭頭上
一個箭頭就是一個迴圈
#include <bits/stdc++.h>
using namespace std;
int main(){
for(int i = 1; i <= 9; i++)
for(int j = 1; j <= 9; j++)
cout << i << " x " << j << " = " << i * j << '\n';
}
進階用法〉
巢狀迴圈
2
Flag
3
給定一連串成績,計算不及格學生的平均
#include <bits/stdc++.h>
using namespace std;
int main(){
int n;
cin >> n;
int fail_sum = 0, fail_n = 0;
for(int i = 0; i < n; i++){
int grade;
cin >> grade;
if(grade < 60){
fail_sum += grade;
fail_n++;
}
}
cout << fail_sum / fail_n << '\n';
}
這段程式會有甚麼問題?
如果沒有不及格的人?
尤其在寫 for 迴圈時,我們有一些約定俗成的習慣
對於用來迭代的計數器,我們習慣用 i 、 j 命名
而對於執行的總次數,我們習慣用 n 、 m 來命名
當然在不同情況下也有特殊的命名法則
像是具有座標類意義,可以用 x 、 y 、 z
對於表格,可以用 col 、 row
這在陣列的單元極為重要,請養成良好的命名習慣
for(int i = 0; i < n; i++){
for(int j = 0; j < m; j++){
//...
}
}
另外對於執行 n 次
我們習慣用從 0 ~ n - 1 的方式迭代
下禮拜會細講這背後的原理
進階用法〉
巢狀迴圈
2
Flag
3
如果沒有不及格的人?
對於某些情況
我們不能保證輸入一定合法
這時候我們可以設一個 flag
標記是不是合法的輸入
以及能不能開始處理資料了
#include <bits/stdc++.h>
using namespace std;
int main(){
int n;
cin >> n;
int fail_sum = 0, fail_n = 0;
bool flag = false
for(int i = 0; i < n; i++){
int grade;
cin >> grade;
if(grade < 60){
flag = true;
fail_sum += grade;
fail_n++;
}
}
if(flag) cout << fail_sum / fail_n << '\n';
else cout << "error\n";
}
給定一連串成績,計算不及格學生的平均
這段程式會有甚麼問題?
進階用法〉
巢狀迴圈
2
Flag
3
在很多時候,並不是所有資料都能直接使用
這時 flag 就能幫我們分辨是否達到處理條件
給定 n 個非零整數,求最小的正整數與最大負整數
#include <bits/stdc++.h>
using namespace std;
int main(){
int n;
cin >> n;
bool positive = false, negative = false;
int min_pos, max_neg;
for(int i = 0; i < n; i++){
int tmp;
cin >> tmp;
if(tmp > 0){
if(!positive){
min_pos = tmp;
positive = true;
}
else if(min_pos > tmp) min_pos = tmp;
}
else{
if(!negative){
max_neg = tmp;
negative = true;
}
else if(max_neg < tmp) max_neg = tmp;
}
}
cout << min_pos << ' ' << max_neg << '\n';
}
透過設定兩個布林變數 positive、negative
可以幫助我們判斷是否出現過正負整數了
※保證有解
出現正數時,如果此前還沒出現過正數
就初始化 min_pos 並把 positive 設成 true
如果出現過了,如果新的數更小就更新 min_pos
反之負數亦然
如果沒有不及格的人?
對於某些情況
我們不能保證輸入一定合法
這時候我們可以設一個 flag
標記是不是合法的輸入
以及能不能開始處理資料了
#include <bits/stdc++.h>
using namespace std;
int main(){
int n;
cin >> n;
int fail_sum = 0, fail_n = 0;
bool flag = false
for(int i = 0; i < n; i++){
int grade;
cin >> grade;
if(grade < 60){
flag = true;
fail_sum += grade;
fail_n++;
}
}
if(flag) cout << fail_sum / fail_n << '\n';
else cout << "error\n";
}
給定一連串成績,計算不及格學生的平均
補充資料
1
語法 & 執行架構
2
逐步執行
3
for vs. while
在很多時候,並不是所有資料都能直接使用
這時 flag 就能幫我們分辨是否達到處理條件
給定 n 個非零整數,求最小的正整數與最大負整數
#include <bits/stdc++.h>
using namespace std;
int main(){
int n;
cin >> n;
bool positive = false, negative = false;
int min_pos, max_neg;
for(int i = 0; i < n; i++){
int tmp;
cin >> tmp;
if(tmp > 0){
if(!positive){
min_pos = tmp;
positive = true;
}
else if(min_pos > tmp) min_pos = tmp;
}
else{
if(!negative){
max_neg = tmp;
negative = true;
}
else if(max_neg < tmp) max_neg = tmp;
}
}
cout << min_pos << ' ' << max_neg << '\n';
}
透過設定兩個布林變數 positive、negative
可以幫助我們判斷是否出現過正負整數了
※保證有解
出現正數時,如果此前還沒出現過正數
就初始化 min_pos 並把 positive 設成 true
如果出現過了,如果新的數更小就更新 min_pos
反之負數亦然
補充資料
補充資料〉
1
condition?
這節課的迴圈,上節課的 if - else 都有包含條件這個概念
然而在那裡面真的只能放條件運算式嗎?
實際上程式碼的執行只會判斷那個區段是否為 true
在 C/C++ 中,true / false 實際上是 非0 / 0
這裡講的可能會被上一堂講師罵
因此我們也可以利用這個性質,在條件式中放上一個整數
當此數等於零時,迴圈就會結束
補充資料〉
1
condition?
這裡講的可能會被上一堂講師罵
先前提到的例子:計算不及格學生的平均
我們需要 f_sum 與 f_num 來計算答案
既然不合法的情況是 f_num == 0
那我們便可以利用 f_num 來判斷輸入是否合法
#include <bits/stdc++.h>
using namespace std;
int main(){
int n;
cin >> n;
int f_num = 0, f_sum = 0;
for(int i = 0; i < n; i++){
int grade;
cin >> grade;
if(grade < 60){
f_num ++;
f_sum += grade;
}
}
if(f_num) cout << f_sum / f_num << '\n';
else cout << "error\n";
}
可不可以用 f_sum?
如果不及格都是零分,那這個判斷會失準
這節課的迴圈,上節課的 if - else 都有包含條件這個概念
然而在那裡面真的只能放條件運算式嗎?
實際上程式碼的執行只會判斷那個區段是否為 true
在 C/C++ 中,true / false 實際上是 非0 / 0
因此我們也可以利用這個性質,在條件式中放上一個整數
當此數等於零時,迴圈就會結束
補充資料〉
1
condition?
這裡講的可能會被上一堂講師罵
打 CF 最常用的
測資一開始會輸入 t 代表會有幾筆測試資料
#include <bits/stdc++.h>
using namespace std;
int main(){
int t;
cin >> t;
while(t--){
//...
}
}
t 會從一開始的 t 跑到 1
在跑到 0 的時候結束迴圈
也就剛好執行了 t 次!
注意,此時隨著迴圈,t 也會不斷改變,無法追溯
因此只有在確定 t 不會再用到的時候才能這樣寫
#include <bits/stdc++.h>
using namespace std;
int main(){
int n;
cin >> n;
int f_num = 0, f_sum = 0;
for(int i = 0; i < n; i++){
int grade;
cin >> grade;
if(grade < 60){
f_num ++;
f_sum += grade;
}
}
if(f_num) cout << f_sum / f_num << '\n';
else cout << "error\n";
}
先前提到的例子:計算不及格學生的平均
我們需要 f_sum 與 f_num 來計算答案
既然不合法的情況是 f_num == 0
那我們便可以利用 f_num 來判斷輸入是否合法
可不可以用 f_sum?
如果不及格都是零分,那這個判斷會失準
補充資料〉
1
condition?
這裡講的可能會被上一堂講師罵
while(t--){
//...
}
?可是 t-- 不是一個算式嗎
是,t-- 確實是一個算式
但放在這裡可以回傳 t 的值
那 --t ?
不可以
無論 ++t、t++、--t、t-- 都會回傳 t 的值
差別在運算子放在前面時,會先改值再回傳
而放在後面時,會先回傳再改值
換句話說
運算子放在前面,會回傳新值
運算子放在後面,會回傳舊值
因此,如果這裡放 t-- ,那麼會迭代 t ~ 0
並在 0 的時候離開迴圈,總共執行 t 次
反之,如果這裡放 --t,那麼會迭代 t - 1 ~ 0
並在 0 的時候離開迴圈,總共執行 t - 1 次
打 CF 最常用的
測資一開始會輸入 t 代表會有幾筆測試資料
t 會從一開始的 t 跑到 1
在跑到 0 的時候結束迴圈
也就剛好執行了 t 次!
注意,此時隨著迴圈,t 也會不斷改變,無法追溯
因此只有在確定 t 不會再用到的時候才能這樣寫
補充資料〉
1
condition?
這裡講的可能會被上一堂講師罵
while(t--){
//...
}
?可是 t-- 不是一個算式嗎
是,t-- 確實是一個算式
但放在這裡可以回傳 t 的值
那 --t ?
不可以
無論 ++t、t++、--t、t-- 都會回傳 t 的值
差別在運算子放在前面時,會先改值再回傳
而放在後面時,會先回傳再改值
換句話說
運算子放在前面,會回傳新值
運算子放在後面,會回傳舊值
因此,如果這裡放 t-- ,那麼會迭代 t ~ 0
並在 0 的時候離開迴圈,總共執行 t 次
反之,如果這裡放 --t,那麼會迭代 t - 1 ~ 0
並在 0 的時候離開迴圈,總共執行 t - 1 次
補充資料〉
1
condition?
這裡講的可能會被上一堂講師罵
其實單純的賦值運算子 ( = ) 也會回傳賦予的值
適當使用這些性質,可以提升程式效率
但請不要走火入魔
寫自己能懂、別人能懂的程式
2
陣列遍歷
補充資料〉
1
condition?
2
陣列遍歷
一般的遍歷方式
for(int i = 0; i < n; i++){
//process arr[i]
}
Python 裡有 for i in list:
C++ 也能做到類似的效果
這裡講的可能會被上一堂講師罵
其實單純的賦值運算子 ( = ) 也會回傳賦予的值
適當使用這些性質,可以提升程式效率
但請不要走火入魔
寫自己能懂、別人能懂的程式
補充資料〉
1
condition?
2
陣列遍歷
for(int i: arr){
//process i
}
這裡的 i 就會變成每一項 arr 裡的值,不需要 arr[i]
補充資料〉
1
condition?
2
陣列遍歷
for(int i: arr){
//process i
}
這裡的 i 就會變成每一項 arr 裡的值,不需要 arr[i]
但單純只是代表值,i 並不能實際操作 arr
for(int j = 0; j < n; j++){
int i = arr[j];
//process i
}
補充資料〉
1
condition?
2
陣列遍歷
for(int &i: arr){
//process i
}
這裡的 i 就會變成每一項 arr 裡的值,不需要 arr[i]
但單純只是代表值,i 並不能實際操作 arr
如果真的需要操作值,可以傳參考就能操作
但同時,沒辦法利用原先arr[i]可以存取前後值的性質
補充資料〉
1
condition?
2
陣列遍歷
for(auto &i: arr){
//process i
}
為了避免搞錯型別,通常這裡會直接用 auto
補充資料〉
1
condition?
2
陣列遍歷
pair<int, int> pos[n];
//...
for(auto &[x, y]: pos){
//process x, y
}
為了避免搞錯型別,通常這裡會直接用 auto
而添加中括號,可以把容器中的變數拆開來表示
補充資料〉
1
condition?
2
陣列遍歷
struct student{
string name;
int id;
pair<int, int> birthday;
};
//...
for(auto &[name, id, birthday]: students){
//process name, id, birthday
}
為了避免搞錯型別,通常這裡會直接用 auto
而添加中括號,可以把容器中的變數拆開來表示
用 auto 的好處是可以容錯容器裡的不同型別