迴圈

LOOOOOP!

臣亮言 @ NTU CSIE

March 8, 2025

Sprout 資訊之芽北區 C 班

在開始之前......

  1. 講師口齒不清,如果他忘記用力說話請大力提醒他
  2. 有任何問題都歡迎直接舉手或在 Slido 提問,不要害怕問問題
  3. 講師喜歡會動來動去的簡報,所以跟之前的簡報會長得不太一樣,如果覺得哪裡不太習慣或有哪些需要改善的歡迎填意見表單或直接跟我說!
  4. 因為是第一次上課所以有什麼建議也請多多指教
  5. 有一些很噁的 coding 習慣不要學
  6. 這個投影片可以四向移動,左右切換主題,上下是同一個主題的前後

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

還真的沒有有些時候我們條件會用到需要執行第一次迴圈才能獲取的資料

CHECK POINT

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

CHECK POINT

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

CHECK POINT

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';
}

透過設定兩個布林變數 positivenegative

可以幫助我們判斷是否出現過正負整數了

※保證有解

出現正數時,如果此前還沒出現過正數

就初始化 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

CHECK POINT

在很多時候,並不是所有資料都能直接使用

這時 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';
}

透過設定兩個布林變數 positivenegative

可以幫助我們判斷是否出現過正負整數了

※保證有解

出現正數時,如果此前還沒出現過正數

就初始化 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 ?

不可以

無論 ++tt++--tt-- 都會回傳 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 ?

不可以

無論 ++tt++--tt-- 都會回傳 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 的好處是可以容錯容器裡的不同型別

LOOP - Sprout C2025

By rain0130

LOOP - Sprout C2025

  • 323