---
tags: TGIRC
---
<style>
html, body, .ui-content {
background-color: #333;
color: #ddd;
}
body > .ui-infobar {
display: none;
}
.ui-view-area > .ui-infobar {
display: block;
}
.markdown-body h1,
.markdown-body h2,
.markdown-body h3,
.markdown-body h4,
.markdown-body h5,
.markdown-body h6 {
color: #ddd;
}
.markdown-body h1,
.markdown-body h2 {
border-bottom-color: #ffffff69;
}
.markdown-body h1 .octicon-link,
.markdown-body h2 .octicon-link,
.markdown-body h3 .octicon-link,
.markdown-body h4 .octicon-link,
.markdown-body h5 .octicon-link,
.markdown-body h6 .octicon-link {
color: #fff;
}
.markdown-body img {
background-color: transparent;
}
.ui-toc-dropdown .nav>.active:focus>a, .ui-toc-dropdown .nav>.active:hover>a, .ui-toc-dropdown .nav>.active>a {
color: white;
border-left: 2px solid white;
}
.expand-toggle:hover,
.expand-toggle:focus,
.back-to-top:hover,
.back-to-top:focus,
.go-to-bottom:hover,
.go-to-bottom:focus {
color: white;
}
.ui-toc-dropdown {
background-color: #333;
}
.ui-toc-label.btn {
background-color: #191919;
color: white;
}
.ui-toc-dropdown .nav>li>a:focus,
.ui-toc-dropdown .nav>li>a:hover {
color: white;
border-left: 1px solid white;
}
.markdown-body blockquote {
color: #bcbcbc;
}
.markdown-body table tr {
background-color: #5f5f5f;
}
.markdown-body table tr:nth-child(2n) {
background-color: #4f4f4f;
}
.markdown-body code,
.markdown-body tt {
color: #eee;
background-color: rgba(230, 230, 230, 0.36);
}
a,
.open-files-container li.selected a {
color: #5EB7E0;
}
</style>
# **37th 南女資研社 C++ 講義 - 1**
[題單](https://docs.google.com/spreadsheets/d/1Yw05k1VBrvykWaJTcpDDcRA3GZ2TfKcwRRITbU0G6hE/edit#gid=0)
[下一篇講義 ouob](https://hackmd.io/@YeZ94/BkuH41UiK)
[新版講義連結](https://hackmd.io/@Tamilala/TngsCppCourse)
## <font color="9CCEF2">C++ 設定</font>
[Dev-C++下載連結](http://orwelldevcpp.blogspot.com/2015/04/dev-c-511-released.html)
### **<font color="B1D6CA">語言切換</font>**
工具(Tools) -> 環境選項(Environment options)-> 語言(Language)-> Chinese(TW)
初始設定為英文,可自行切換為中文或其他語言
### **<font color="B1D6CA">版面配色</font>**
工具 -> 編輯器選項 -> 語法顏色
可以自行調整文字和背景,或是點擊左下角**顏色快速設定**
### **<font color="B1D6CA">字體大小</font>**
工具 -> 編輯器選項 -> 字型
在這裡可以調整字體大小,而左側的字型建議使用預設的 Consolas 就好,不用多做更動
## <font color="9CCEF2">Hello World</font>
在 Dev-C++ 中,點擊檔案 -> 開新檔案 -> 原始碼 / 檔案下方空白圖示  -> 原始碼 / 快捷鍵(CTRL+N) ,即可開啟一個空白的檔案,按下 F12 或是  就能進行編譯
### **<font color="B1D6CA">C++ 基本架構</font>**
```cpp=
#include <iostream>
using namespace std;
int main(){
return 0;
}
```
以上為一個程式碼的基本架構,在之後都會使用到它,請各位務必記起來,而 `{` 除了放在
`int main()` 旁,也有有人會將 `{` 放至下一行,可依個人喜好決定
* `int` 用於宣告 `main()` 最終的回傳值會是一個整數
* `main()` 是一個主函數,名字叫 **main**,而後方的 `()` 表示它是一個函數,函數後用 `{}` 包起來的區塊會放這個函數要執行的命令
* `return 0` 表示這段指令已經結束,回報 0
* `#include <iostream>` 表示將 `<iostream>` 這個標頭檔引入這支程式內
註: `<iostream>` 這個標頭檔中,定義了 `cout` 、 `cin` 、 `cerr` 等函式,要使用 `cout` (標準輸出)就必須將它引入,否則編譯器會不知道 `cout` 是什麼東西
### **<font color="B1D6CA">輸入與輸出</font>**
首先是最基本的<font color="F5F6B6">**輸出**</font>
```cpp=
#include <iostream>
using namespace std;
int main(){
cout<<"Hello World"<<endl; //輸出Hello World這個字串
//endl 中的 l 是 L 不是數字 1
return 0;
}
```

透過 `cout` (標準輸出)再搭配 `<<` 這兩個指令就能印出想印出的訊息,文字要放在 `""` 中,而後方的 `endl` 表示的是換行,也可用 `"\n"` 替代,在指令的最後請記得要附上 `;` 表示結束(C++ 中每行指令後都要加上分號)
:::spoiler <font color="FEA0A0">**題目**</font>
1. [Zerojudge d483: hello, world](https://zerojudge.tw/ShowProblem?problemid=d483)
:::
>
:::spoiler <font color="FEA0A0">**題解**</font>
1. [Zerojudge d483: hello, world](https://zerojudge.tw/ShowProblem?problemid=d483)
```cpp=
#include <iostream>
using namespace std;
int main(){
cout<<"hello, world";
return 0;
}
```
單純的輸出而已,有錯的可能是輸出的格式錯誤,可以直接複製範測的再貼上,就能確保格式正確了
:::
---
接下來是<font color="F5F6B6">**輸入**</font>
```cpp=
#include <iostream>
using namespace std;
int main(){
string s; //宣告字串s
cin>>s; //將輸入內容存入s
cout<<s<<"\n"; //輸出s的內容
return 0;
}
```
先用 `string` 來宣告一個字串叫 **s**,再透過 `cin` 和 `>>` 輸入想列印出來的文字,直到遇到換行或是空白時停止,接著透過剛剛介紹的輸出,列印輸入的文字以及換行
:::spoiler <font color="FEA0A0">**題目**</font>
1. [Zerojudge a001: 哈囉](https://zerojudge.tw/ShowProblem?problemid=a001)
:::
>
:::spoiler <font color="FEA0A0">**題解**</font>
1. [Zerojudge a001: 哈囉](https://zerojudge.tw/ShowProblem?problemid=a001)
```cpp=
#include <iostream>
using namespace std;
int main(){
string s;
cin>>s;
cout<<"hello, "<<s<<"\n";
return 0;
}
```
相較於上題多了個輸入,透過 string 宣告一個字串 s,再進行輸入即可,輸出時先輸出題目要求的 `hello, `,再輸出裝載輸入訊息的變數 s
:::
---
## <font color="9CCEF2">四則運算</font>
在使用 C++ 進行運算前,要先知道該如何進行整數的輸入與輸出
### **<font color="B1D6CA">整數的輸入與輸出</font>**
```cpp=
#include <iostream>
using namespace std;
int main(){
int num; //宣告整數num
cin>>num; //將輸入內容存入num
cout<<"Hi! "<<num<<"\n";
//輸出"Hi! "之後,再輸出num,再輸出一個換行 ("\n")
return 0;
}
```

運用上面講過的 `int` 先宣告一個整數變數,名字叫 **num**,以上圖為例它承載了我們輸入給它的數字 **111**,最後被 cout 輸出出來,接在 **Hi!** 的後面
`int num;` 要放在 `cin >> num;` 之前,由於程式是逐行由上到下運行的,輸入時還沒有 **num** 這個變數存在,將導致編譯錯誤
:::info
變數命名規則
1. 變數名稱不可重複
2. 英文字大小寫、數字以及底線(\_)都能作使用,大小寫字母視為不同
ex. a1233、dd_12、\_2015ad
3. 數字不能在第一位,也不能是在 C++ 中具備涵義的詞
ex. 2021、12dd、return、int
PS. 命名盡量使用清楚的名稱或幫助自己快速明白用途的文字,否則在撰寫大量的程式碼時容易遺忘
:::
詳細的變數命名規則,可以參考 -> [補充](https://hackmd.io/@Tamilala/naming_rule)
### **<font color="B1D6CA">常用運算符號</font>**
* **加法**:x + y
* **減法**:x - y
* **乘法**:x \* y
* **除法**:x / y (小數點無條件捨去)
* **取餘數(mod)**:x % y (5%3 == 2)
### **<font color="B1D6CA">其他運算函式</font>**
可引用數學函式庫,使用更多的功能
`#include <cmath>`
**x的y次方**:pow(x,y)
**根號x**:sqrt(x)
<font color="F5F6B6">**範例 1**</font>
計算 x^2^+1
```cpp=
#include <iostream>
#include <cmath>
using namespace std;
int main(){
int x; //宣告整數變數x
cin>>x; //將輸入內容存入x
cout<<pow(x,2)+1<<"\n";
//輸出x的平方 ( pow(x,2) ) 加一之後,再輸出一個換行 ("\n")
return 0;
}
```

在程式中,和平常進行運算時一樣,都是先乘除後加減,想先計算加減時,可用 `()` 包起來,最內層的 `()` 會先進行運算
<font color="F5F6B6">**範例 2**</font>
```cpp=
#include <iostream>
using namespace std;
int main(){
cout<<(6+(7+8*(9-4)))*2<<"\n";
//先執行最內層括號內的9-4等於5
//然後5再先乘除後加減,將5*8等於40
//40+7等於47
//至此,將47加上6等於53,最後再乘上最外層的2=106
return 0;
}
```
:::spoiler <font color="FEA0A0">**題目**</font>
1. [Zerojudge a002: 簡易加法](https://zerojudge.tw/ShowProblem?problemid=a002)
2. [Zerojudge d049: 中華民國萬歲!](https://zerojudge.tw/ShowProblem?problemid=d049)
3. [Zerojudge d073: 分組報告](https://zerojudge.tw/ShowProblem?problemid=d073)
:::
>
:::spoiler <font color="FEA0A0">**題解**</font>
1. [Zerojudge a002: 簡易加法](https://zerojudge.tw/ShowProblem?problemid=a002)
```cpp=
#include<iostream>
using namespace std;
int main(){
int a,b; //宣告整數變數a與b
cin>>a>>b; //輸入a與b
cout<<a+b; //輸出(a+b)的值
return 0;
}
```
2. [Zerojudge d049: 中華民國萬歲!](https://zerojudge.tw/ShowProblem?problemid=d049)
```cpp=
#include<iostream>
using namespace std;
int main(){
int n; //宣告整數變數n
cin>>n; //輸入n
cout<<n-1911; //輸出(n-1911)的值
return 0;
}
```
註:民國年=西元年-1911
3. [Zerojudge d073: 分組報告](https://zerojudge.tw/ShowProblem?problemid=d073)
```cpp=
#include<iostream>
using namespace std;
int main(){
int n;
cin>>n;
cout<<(n-1)/3+1;
return 0;
}
```
這題稍微複雜些,可以先依每個座號列出其組別,然後在觀察其關係。
`1/3` 為 `0` ,在第 `1` 組
`2/3` 為 `0` ,在第 `1` 組
`3/3` 為 `1` ,在第 `1` 組
`4/3` 為 `1` ,在第 `2` 組
`5/3` 為 `1` ,在第 `2` 組
`6/3` 為 `2` ,在第 `2` 組
`7/3` 為 `2` ,在第 `3` 組
...
可以觀察出,每個座號先向後移1後,再除以3,然後再加上1就是他的組別了。
改寫成運算式即為: `(n+1)/3 +1`
<!--
4. [d039: 11044 - Searching for Nessy](https://zerojudge.tw/ShowProblem?problemid=d039)
```cpp=
#include<iostream>
using namespace std;
int main(){
int n,a,b;
cin>>n;
while(n--){ //重複輸入n筆測資
cin>>a>>b; //輸入a與b
cout<<(a/3)*(b/3)<<"\n";
}
return 0;
}
```
將每個點隔三格放一個,盡量使面積不重疊即為最佳解。
一樣可列出邊長為 `n` 時,最少需要放幾個點,以觀察其關係(需要最少 `n/3` 個點)。
-->
:::
---
### **<font color="B1D6CA">賦值</font>**
除了使用 `cin` 給予變數一個值以外,可以使用 `=` 直接賦予變數一個初始值
```cpp=
#include <iostream>
using namespace std;
int main(){
int num,num2=2,num3;
//宣告num與num3,不賦予初始值
//宣告num2,並賦予初始值為2
//此時num2的值為2
num3=3;
//將3賦予num3,此時num3的值為3
cin>>num;
//將輸入值賦予給num
cout<<num<<" + "<<num2<<" + "<<num3<<" = ";
//用雙引號「"」框起來的為字串,沒框起來的是變數依各符號功能做處理
//依序輸出 num、「+」符號、num2、「+」符號、num3
cout<<num+num2+num3<<"\n";
//輸出 num+num2+num3 的「值」
return 0;
}
```

也可讓一個變數的值 = 另一個變數的值(將一個變數的值賦予給另一個變數),甚至做運算
```cpp=
#include <iostream>
using namespace std;
int main(){
int num=3,num2,num3;
//宣告num,並賦予初始值3
num2=num;
//將num的值3賦予num2,此時num2的值為3
num3=num+num2*2;
//將num+num2*2,也就是等於3(num的值)+3(num2的值)*2
//所以3+3*2=3+6=9,將9賦予num3
//此時num3的值為9
cout<<"num: "<<num<<"\nnum2: "<<num2<<"\nnum3: "<<num3<<"\n";
//「\n」表示換行
return 0;
}
```

表示 `num = num + 3;` 時,可用 `num += 3;` 其他 `-`、`*` 、`/`、`%` 都可用這種方法
而 `num++;` 會等於 `num += 1;`,`num--;` 會等於 `num -= 1;`
## **<font color="9CCEF2">資料型態</font>**
<font color="F5F6B6">**int**</font> 的範圍是 $-2^{31} \le int \lt 2^{31}$ 即 $-2,147,483,648 \le int \le 2,147,483,647$
當運算的數字超過 **int** 的範圍時,可使用 <font color="F5F6B6">**long long**</font>
可儲存範圍到 $2^{63}-1 = 9,223,372,036,854,775,807$ 大約是 $9\times 10^{18}$
一般常數預設是 **int** 型態,若要改成 **long long** 型態時,要在常數後加上 `LL`
```cpp=
#include <iostream>
using namespace std;
int main(){
long long num=123456789; //10^10
cout<<"溢位(未加LL) "<<num*2<<"\n";
cout<<"無溢位(有加LL) "<<num*2LL<<"\n";
return 0;
}
```
:::info
當運算數字超過範圍時稱作溢位,儘管結果會在範圍中,只要過程出現溢位,最終的答案就會有問題,因此在做運算時要多加小心
如果超過 **long long** 範圍的話,就要使用**大數**來解題
:::
關於溢位,有興趣可以參考[補充](https://hackmd.io/@Tamilala/overflow)。
<font color="F5F6B6">**unsigned int**</font> 是用來宣告正整數,範圍是 $0 \le unsigned$ $int \le 2^{32}-1$
即 $0 \le unsigned$ $int \le 4,294,967,295$
除了 `int` 宣告常數以外,還有其他種宣告的方式:
* <font color="F5F6B6">**char**</font>:宣告字元,可輸入單個字元,數字無法和常數一樣直接進行運算,以 **Ascii** 進行轉換
* <font color="F5F6B6">**string**</font>:宣告字串,可輸入一串句子,直到**空格**或**換行**,與 char 相同皆為文字型態
* <font color="F5F6B6">**bool**</font>:宣告布林值,範圍 0(false)、1(true)
* <font color="F5F6B6">**float**</font>:宣告浮點數,即可使用小數
* <font color="F5F6B6">**double**</font>:宣告倍精度浮點數,比 float 還要精準不少,進行小數的運算時,建議都以此宣告
:::spoiler <font color="FEA0A0">**題目**</font>
1. [98 / c - Speed of Light
](https://toj.tfcis.org/oj/pro/98/)
:::
## **<font color="9CCEF2">ASCII</font>**
全名 **A**merican **S**tandard **C**ode for **I**nformation **I**nterchange,美國資訊交換標準代碼
[ASCII 維基百科](https://zh.wikipedia.org/wiki/ASCII)
- 第 **0~32** 號、第 127 號(共 34 種) -> 通訊專用或<font color="F5F6B6">控制字元</font>(LF換行、DEL刪除等)
- 第 **33~126** 號(共 94 個) -> 可顯示字元
- 第 **48~57** 號 -> 0~9 十個阿拉伯數字
- 第 **65~90** 號 -> 大寫字母
- 第 **97~122** 號 -> 小寫字母
- 其他為標點符號、運算符號等

運用以下程式碼即可知道 **a** 的 ASCII 編號為多少:
```cpp=
#include <iostream>
using namespace std;
int main(){
char c='a';
//宣告字元變數c,並賦予初始值為字母'a'
//此時c的值為'a'
cout<<c<<" : "<<int(c)<<"\n";
//int(c):將c的值'a',以整數型態輸出
//也就是輸出'a'的ASCII碼97
return 0;
}
```

也可從 ASCII 編號轉換成字元,以及運用加減法改變字元值
```cpp=
#include <iostream>
using namespace std;
int main(){
int c=97;
//宣告整數變數c,並賦予初始值97
cout<<c<<" : "<<char(c)<<"\n";
//char(c):將c的值97,以字元型態輸出
//也就是輸出ASCII碼中,97所代表的字元'a'
char C='a';
//宣告字元變數C,並賦予初始值為字母'a'
//注意,名稱大小寫不同即視為不同變數,所以C跟c是不一樣的!
cout<<C<<" - 31 = "<<char(C-31)<<"\n";
//char(C-31):將C的ASCII碼減掉31的值,以字元型態輸出
//'a'的ASCII碼為97,減掉31為66,代表'B'
//也就是輸出ASCII碼中,66代表的字元'B'
return 0;
}
```

:::spoiler <font color="FEA0A0">**題目**</font>
1. 輸出 `*`、`8`、`}`、`H` 的 ASCII 編號
2. 讓輸入的字母都後移 3 位吧!(超出 Z 或是 z 時,從 A 或 a 開始)
3. [TOJ 101 / e' - English Alphabet Prime](https://toj.tfcis.org/oj/pro/101/)
:::
---
## **<font color="9CCEF2">if else</font>**
### **<font color="B1D6CA">比較運算子</font>**
等於 `==`
大於 `>`
小於 `<`
大於等於 `>=`
小於等於 `<=`
不等於 `!=`
### **<font color="B1D6CA">基本架構</font>**
一條道路上可能會出現分岔,根據情況不同要選擇該往哪邊走,因此就會需要使用 if else 這個語法
```cpp=
#include <iostream>
using namespace std;
int main(){
if(條件句){
指令;
}
else{
指令;
}
return 0;
}
```
if 後方的括號中要放入一段條件句,像是 `a>10` ,達成這個條件後要執行的事要放在大括號中,而未滿足這個條件就會執行 else 中的指令
但如果只有滿足這個條件的需要做事,可以不用加上 else
### **<font color="B1D6CA">**else if**</font>**
當題目具有多個條件時,顯然單單只有一個 if 來判斷是不夠的,因此就會需要用到 <font color="F5F6B6">**else if**</font>
```cpp=
#include <iostream>
using namespace std;
int main(){
if(條件句){
指令;
}
else if(條件句){
指令;
}
else{
指令;
}
return 0;
}
```
只要 if 在首位,要在後方插入多少個 else if 都沒問題
:::info
若只需執行一條程式碼,後方的 `{}` 可不加,但剛開始練習時建議養成加上 `{}` 的好習慣
`if` 連接 `else if` 與 `else if` 連接 `else` 中,都不可在中間加上程式碼,它們都要緊接在後
:::
### **<font color="B1D6CA">**邏輯運算子**</font>**
用於串接多個條件式
且 `&&` -> 同時成立
或 `||` -> 一個成立即可
否定 `!` -> 反轉
<font color="F5F6B6">**範例 1**</font>
```cpp=
#include <iostream>
using namespace std;
int main(){
int a=3,b=6;
if(a<4 && b%2==0){
cout<<"ouob\n";
}
else{
cout<<"onoq\n";
}
return 0;
}
```


<font color="F5F6B6">**範例 2**</font>
```cpp=
#include <iostream>
using namespace std;
int main(){
int a,b;
cin>>a>>b;
if(!(a%b)){
cout<<"a 可以被 b 整除\n";
}
else{
cout<<"a 不可以被 b 整除\n";
}
return 0;
}
```


當 a 能被 b 整除時,a%b 會等於 0,而被反轉後 0 會變成 1 表示正確(true),因此會執行 if 中的指令
<font color="F5F6B6">**範例 3**</font>
```cpp=
#include <iostream>
using namepsace std;
int main(){
int num,num2;
cin>>num>>num2;
if(num>3 && num<20 || num2%2!=0){
cout<<"ouob\n";
}
else if(num2 >100){
cout<<"num2 is so bigggggg\n";
}
return 0;
}
```


邏輯運算子可以一次使用多個,會先執行 `&&` 再執行 `||`,使用 `()` 就能先執行 `()` 中的指令
除了 if else 之外,邏輯運算子也可以做到條件判斷
:::info
條件式 ? 符合條件 : 不符合條件
:::
<font color="F5F6B6">**範例 1**</font>
```cpp=
#include <iostream>
using namespace std;
int main(){
int a=3;
cout<<(a%2 == 0 ? "a 是偶數":"a 是奇數")<<"\n";
return 0;
}
```

:::spoiler <font color="FEA0A0">**題目**</font>
1. [Zerojudge d064: ㄑㄧˊ 數?](https://zerojudge.tw/ShowProblem?problemid=d064)
2. [Zerojudge a003: 兩光法師占卜術](https://zerojudge.tw/ShowProblem?problemid=a003)
3. [Zerojudge d485: 我愛偶數](https://zerojudge.tw/ShowProblem?problemid=d485)
4. [Zerojudge d065: 三人行必有我師](https://zerojudge.tw/ShowProblem?problemid=d065)
5. [Zerojudge d066: 上學去吧!](https://zerojudge.tw/ShowProblem?problemid=d066)
6. [Zerojudge a006: 一元二次方程式](https://zerojudge.tw/ShowProblem?problemid=a006)
:::
---
## **<font color="9CCEF2">迴圈 - while</font>**
### **<font color="B1D6CA">基本架構</font>**
當我們要執行重複的指令時,一直不斷複製相同的程式碼會很占版面,而迴圈就能幫助我們處理這些需要重複的指令
```cpp=
#include <iostream>
using namespace std;
int main(){
while(條件句)
{
循環指令;
}
return 0;
}
```
:::info
條件句在迴圈中擔任非常重要的角色,如果出錯就會迴圈可能就會變成無窮迴圈,無法停下
e.g. 當判斷條件為while(1)時,因為1永遠是true,所以迴圈會無限循環
開始跑之前才會檢查一次是否符合條件,因此途中不符合仍然會持續運作
:::
<font color="F5F6B6">**範例 1**</font>
試著印出 1~5 吧
```cpp=
#include <iostream>
using namespace std;
int main(){
cout<<"1\n";
cout<<"2\n";
cout<<"3"<<"\n";
cout<<4<<"\n";
cout<<5<<"\n";
return 0;
}
```

使用 <font color="F5F6B6">**while**</font>:
```cpp=
#include <iostream>
using namespace std;
int main(){
int i=1;
while(i<=5){
cout<<i<<"\n";
i++; //i=i+1
}
return 0;
}
```

因為想要執行重複輸出這個動作,所以使用迴圈並設定一個 i 變數,當 i <= 5 時輸出 i,因為 i = 1,如果想每次輸出的數字都往上加 1,就要再放一個 `i++;` 的指令或是 `i+=1;`,表示 i=i+1
<font color="F5F6B6">**範例 2**</font>
重複輸入某數,如果它小於 3,輸出它加 3 的結果,如果大於 3,輸出它乘 3 的結果
```cpp=
#include <iostream>
using namespace std;
int main(){
int num;
while(cin>>num){
if(num<3){
cout<<"num<3 : num+3 = "<<num+3<<"\n";
}
else if(num>3){
cout<<"num>3 : num*3 = "<<num*3<<"\n";
}
}
return 0;
}
```

**在不知道執行次數,但知道何種條件下要終止迴圈時,可以使用 <font color="F5F6B6">**while**</font>**,但一定要注意的是 `return 0;` 不能放在迴圈裡面,否則只會執行一次,因為沒有處理 num == 3 的情況,所以不會輸出任何東西
### **<font color="B1D6CA">do while</font>**
和 while 相比,while 是先判斷條件是否成立,再執行程式,而 do while 則是先執行程式,再去判斷條件是否成立
```cpp=
#include <iostream>
using namespace std;
int main(){
do
{
循環指令;
}
while(條件句);
return 0;
}
```
透過下方的例子可以更明顯的知道兩者的不同
<font color="F5F6B6">**while**</font>
```cpp=
#include <iostream>
using namespace std;
int main(){
int i=0;
while(i>0){
i--; //i=i-1
}
cout<<i<<"\n";
return 0;
}
```

<font color="F5F6B6">**do while**</font>
```cpp=
#include <iostream>
using namespace std;
int main(){
int i=0;
do
{
i--; //i=i-1
}
while(i>0);
cout<<i<<"\n";
return 0;
}
```

:::info
使用 do while 時務必記得要加上 `;`
但 while 只要做一下處理,也能達到和 do while 一樣的結果,不用特別去記 do while 的用法也沒關係
:::
:::spoiler <font color="FEA0A0">**題目**</font>
1. [Zerojudge a004: 文文的求婚](https://zerojudge.tw/ShowProblem?problemid=a004)
2. [Zerojudge a024: 最大公因數(GCD)](https://zerojudge.tw/ShowProblem?problemid=a024)
3. [Zerojudge a010: 因數分解](https://zerojudge.tw/ShowProblem?problemid=a010)
4. [Zerojudge a005: Eva 的回家作業](https://zerojudge.tw/ShowProblem?problemid=a005)
5. [Zerojudge b572: 忘了東西的傑克](https://zerojudge.tw/ShowProblem?problemid=b572)
6. [Zerojudge d490: 我也愛偶數](https://zerojudge.tw/ShowProblem?problemid=d490)
7. [Zerojudge d039: 11044 - Searching for Nessy](https://zerojudge.tw/ShowProblem?problemid=d039)
:::
---
## **<font color="9CCEF2">迴圈 - for</font>**
### **<font color="B1D6CA">基本架構</font>**
```cpp=
#include <iostream>
using namespace std;
int main(){
for(執行前的事; 執行條件; 下一輪要做的事){
指令;
}
return 0;
}
```
:::info
for 是迴圈的第二種寫法,常用於指定執行次數的時候
當一次要執行很多事時,在同個段落中可以用逗號分隔,而如果什麼事都不需要做,可以只留下分號就好
:::
<font color="F5F6B6">**範例 1**</font>
試著印出 1~5 吧
```cpp=
#include <iostream>
using namespace std;
int main(){
int i;
for(i = 1; i <= 5; i++){
cout<<i<<"\n";
}
return 0;
}
```

這段程式碼表示,一開始先宣告一個 i,在迴圈開始前先把 i 指定為 1,當 i <= 5 時,i = i+1,每跑一次就加一次
<font color="F5F6B6">**範例 2**</font>
找看看 n 是否為完全平方數
```cpp=
#include <iostream>
using namespace std;
int main(){
int n;
cin>>n;
int i;
for(i=1; i*i<n; i++){
}
if(i*i == n){
cout<<"n是完全平方數\n";
}
else {
cout<<"n不是完全平方數\n";
}
return 0;
}
```


因為我們要找的是 i,迴圈內不需要額外跑指令,所以可以不放東西
<font color="F5F6B6">**範例 3**</font>
輸入 n 個數,計算有多少個數字大於 n
```cpp=
#include <iostream>
using namespace std;
int main(){
int n,i,ans;
cin>>n;
for(i=0, ans=0; i < n; i++){
int a;
cin>>a;
if(a > n){
ans++;
}
}
cout<<ans<<"\n";
return 0;
}
```

在同個段落中,要執行多件事可以用逗號分割
:::spoiler <font color="FEA0A0">**題目**</font>
1. [Zerojudge a244: 新手訓練 ~ for + if](https://zerojudge.tw/ShowProblem?problemid=a244)
2. [Zerojudge d074: 電腦教室](https://zerojudge.tw/ShowProblem?problemid=d074)
3. [Zerojudge d498: 我不說髒話](https://zerojudge.tw/ShowProblem?problemid=d498)
4. [Zerojudge a148: You Cannot Pass?!](https://zerojudge.tw/ShowProblem?problemid=a148)
5. [Kattis FizzBuzz](https://open.kattis.com/problems/fizzbuzz)
6. [TOJ 577 / 力量](https://toj.tfcis.org/oj/pro/577/)
:::
---
### **<font color="B1D6CA">巢狀迴圈</font>**
有些時候會需要在迴圈中再放一層迴圈,分成彼此有關聯以及彼此沒關聯兩種
<font color="F5F6B6">**內外層不相干**</font>
試著透過 for 畫出一個邊長為 n 的正方形吧

想輸出這樣的矩形,可以分成兩部分,輸出 n 個 \* 的寬,再輸出 n 排,又可以說是分成一個會重複輸出 n 個 * 的迴圈跟一個會反覆輸出 n 排有 n 個 * 的迴圈
一個個步驟來看,先做出一層輸出寬的迴圈
```cpp=
#include <iostream>
using namespace std;
int main(){
int n;
cin>>n;
int i;
for(int i=0;i<n;i++){
cout<<'*';
}
return 0;
}
```
現在做好了寬,接著重複輸出 n 排寬
```cpp=
#include <iostream>
using namespace std;
int main(){
int n;
cin >> n;
int i,j;
for(i = 0; i < n; i++){
for(j = 0; j < n; j++){
cout<<"*";
}
}
return 0;
}
```
最後放入換行,因為換行要等寬都輸出完,所以只能放在外層的迴圈,當然也可以放在內層中看看最後輸出會變怎樣
```cpp=
#include <iostream>
using namespace std;
int main(){
int n;
cin >> n;
int i,j;
for(i = 0; i < n; i++){
for(j = 0; j < n; j++){
cout<<"*";
}
cout<<"\n";
}
return 0;
}
```
迴圈沒有必要一定要塞在一層內,像這樣的多層迴圈反而能更好閱讀以及更輕易的抓出錯誤
<font color="F5F6B6">**內外層相干**</font>
知道了該如何做出三角形,接著試著輸出邊長為 n 的直角三角形吧

透過觀察,我們可以知道三角形在第一層時輸出一個 * 第二層兩個,以此類推,這次我們先建好一個高,長為 n
```cpp=
#include <iostream>
using namespace std;
int main(){
int n;
cin>>n;
for(int i=0;i<n;i++){
}
return 0;
}
```
和輸出矩形的步驟一樣,放入內層的迴圈與換行
```cpp=
#include <iostream>
using namespace std;
int main(){
int n;
cin>>n;
for(int i=0;i<n;i++){
for(int j=0;j<n;j++){
cout<<"*";
}
cout<<"\n";
}
return 0;
}
```
從前面的觀察可以知道三角形在第一層時輸出一個 \*,第二層兩個,以此類推,因此把這個邏輯套到程式內,i 跟三角形每層輸出的 * 一樣,每次都加一,所以可以把內層的 n 替換成 i
```cpp=
#include <iostream>
using namespace std;
int main(){
int n;
cin>>n;
for(int i=0;i<n;i++){
for(int j=0;j<i;j++){ //n 換成 i
cout<<"*";
}
cout<<"\n";
}
return 0;
}
```
但現在又有個問題, i 一開始是 0,如果這樣的話,內層的迴圈就達不到設定的條件,如果要解決這個問題,那就讓 i 直接變成從 1 開始就搞定了
```cpp=
#include <iostream>
using namespace std;
int main(){
int n;
cin>>n;
for(int i=1;i<=n;i++){ //令 i == 1
for(int j=0;j<i;j++){
cout<<"*";
}
cout<<"\n";
}
return 0;
}
```
只是當 i 是 1 時,使用 i < n 的話,會少掉一次,這時的條件要變成 i <= n,當然如果要改成 i < n+1 也可以
接下來試試該如何輸出倒過來且底和高長度是 n 的直角三角形,以及高是 n 的等腰三角形,輸出圖案如下圖所示:
高度長 n 的倒直角三角形

```cpp=
#include <iostream>
using namespace std;
int main(){
int n;
cin>>n;
for(int i=1;i<=n;i++){
for(int j=0;j<=n-i;j++){
cout<<"*";
}
cout<<"\n";
}
return 0;
}
```
高是 n 的等腰三角形

```cpp=
#include <iostream>
using namespace std;
int main(){
int n;
cin>>n;
for(int i=1;i<=n;i++){
for(int j=0;j<i;j++){
cout<<"*";
}
cout<<"\n";
}
for(int i=1;i<=n;i++){
for(int j=0;j<n-i;j++){
cout<<"*";
}
cout<<"\n";
}
return 0;
}
```
<font color="F5F6B6">**更複雜的巢狀迴圈**</font>
輸出一個邊長為 n 的平行四邊形

透過這張圖,我們可以分析出,它是由一個倒直角三角形加上一個正方形所組成的

運用輸出正方形的程式碼(在練習時請自己重新打一個,不要用複製貼上的方式,否則無法增加熟練度)
```cpp=
#include <iostream>
using namespace std;
int main(){
int n;
cin >> n;
int i,j;
for(i = 0; i < n; i++){
for(j = 0; j < n; j++){
cout<<"*";
}
cout<<"\n";
}
return 0;
}
```
和倒直角三角形的程式碼
```cpp=
#include <iostream>
using namespace std;
int main(){
int n;
cin>>n;
for(int i=1;i<=n;i++){
for(int j=0;j<=n-i;j++){
cout<<"*";
}
cout<<"\n";
}
return 0;
}
```
將它們結合起來即可得到一個邊長為 n 的平行四邊形,由於會先輸出倒直角三角形,把輸出的指令放在上方
```cpp=
#include <iostream>
using namespace std;
int main(){
int n;
cin>>n;
for(int i=1;i<=n;i++){
for(int j=0;j<=n-i;j++){
cout<<"*";
}
cout<<"\n";
}
return 0;
}
```
接著放入正方形的指令
```cpp=
#include <iostream>
using namespace std;
int main(){
int n;
cin>>n;
for(int i=1;i<=n;i++){
for(int j=0;j<=n-i;j++){
cout<<" ";
}
for(int j=0;j<=n-i;j++){
cout<<"*";
}
cout<<"\n";
}
return 0;
}
```
但很重要的一點是,倒三角形的高是 n-1,可是平行四邊形是 n,因此要修改一下倒三角形的條件式
```cpp=
#include <iostream>
using namespace std;
int main(){
int n;
cin>>n;
for(int i=1;i<=n;i++){
for(int j=0;j<n-i;j++){ //修改成 j < n-1
cout<<" ";
}
for(int j=0;j<n;j++){
cout<<"*";
}
cout<<"\n";
}
return 0;
}
```
將倒直角三角形中的 j <= n-i 的部分,修改成 j < n-i,這樣就能只輸出邊長為 n-1 的倒直角三角形了
:::spoiler <font color="FEA0A0">**題目**</font>
1. 反覆輸出高是 n 的正三角形

2. [Zerojudge d649: 數字三角形](https://zerojudge.tw/ShowProblem?problemid=d649)
3. [Zerojudge c013: 00488 - Triangle Wave](https://zerojudge.tw/ShowProblem?problemid=c013)
4. [TOJ 104 / 星星樹](https://toj.tfcis.org/oj/pro/104/)
5. [TOJ 110 / 六芒星的咒符](https://toj.tfcis.org/oj/pro/110/)
:::
---
## **<font color="9CCEF2">switch、break、continue</font>**
### **<font color="B1D6CA">switch</font>**
switch 是多重條件判斷的陳述式,在某些情況下會比 `if else` 來的有效率
<font color="F5F6B6">**基本架構**</font>
```cpp=
#include <iostream>
using namespace std;
int main(){
switch(變數名稱或運算式){
case 符合數字或字元:
陳述句一;
break;
case 符合數字或字元:
陳述句二;
break;
default:
陳述句三;
break;
}
return 0;
}
```
在一開始的括號中,存放著數值或是運算式,找到符合 `case` 的值後就會執行指令,直到遇到 `break` 才會離開 switch,`case` 沒有加上 `break` 的話,就會一路跑下去,直到遇到下一個符合條件的 `case` 中的 `break` 或是跑入 `default` 中並執行指令,如果不需要 `default` 執行什麼事,則可以省略
<font color="F5F6B6">**範例 1**</font>
為了食物的品質,海綿寶寶被要求做出的美味蟹堡的美味分數都要在 7 分以上,滿分 10 分,否則通通都要拿去倒掉,派大星則是過來協助幫忙把要丟掉的美味蟹堡分隔開來,但顯然冒失的派大星無法勝任這份工作,請撰寫一份程式幫忙他吧!
```cpp=
#include <iostream>
using namespace std;
int main(){
int score;
while(cin>>score){
switch(score){
case 10:
cout<<"It's really d3lic1Ou2.\n";
break;
case 9:
cout<<"It's really d3lic1Ou2.\n";
break;
case 8:
cout<<"It's really d3lic1Ou2.\n";
break;
case 7:
cout<<"It's really d3lic1Ou2.\n";
break;
default:
cout<<"~!@#$%^&*()_+\n";
break;
}
}
return 0;
}
```

<font color="F5F6B6">**範例 2**</font>
呈上題,每次都需要一行一行輸出太累了,可以設定一個區間,只要變數的值符合這個區間就執行指令
```cpp=
#include <iostream>
using namespace std;
int main(){
int score;
while(cin>>score){
switch(score){
case 7 ... 10:
cout<<"It's really d3lic1Ou2.\n";
break;
default:
cout<<"~!@#$%^&*()_+\n";
break;
}
}
return 0;
}
```
:::info
撰寫時要記得,... 的左右都需要加上空白
:::

除此之外,`default` 也可以放在最上方
```cpp=
#include <iostream>
using namespace std;
int main(){
switch(變數名稱或運算式){
default:
陳述句三;
break;
case 符合數字或字元:
陳述句一;
break;
case 符合數字或字元:
陳述句二;
break;
}
return 0;
}
```
`default` 在最下方時,可以不用加上 `break`,但置於最上方時一定要加上 `break`,因此建議都加上 `break` 避免執行錯誤
:::spoiler <font color="FEA0A0">**題目**</font>
1. [Zerojudge a053: Sagit's 計分程式](https://zerojudge.tw/ShowProblem?problemid=a053)
2. [Zerojudge a244: 新手訓練 ~ for + if](https://zerojudge.tw/ShowProblem?problemid=a244) (請試著用switch解)
:::
---
### **<font color="B1D6CA">break</font>**
當達到一個條件時,我們不希望某段指令持續運作,此時就可以用 `break` 離開程式碼
<font color="F5F6B6">**範例 1**</font>
```cpp=
#include <iostream>
using namespace std;
int main(){
int i=0;
while(true){
if(i==37){
cout<<i<<" !!! ouob\n";
break;
}
cout<<i<<" onoq\n";
i++;
}
return 0;
}
```

只要迴圈是 true,這段迴圈就會一直不間斷的執行下去,當 i 是 37 時,就會輸出 ` 37 !!! ouob ` 再跳出迴圈
運用 `break` 的結果,相較於讓程式全部跑完再執行指令來的有效率不少,是相當常用的指令,一般會用於 switch 或是迴圈,在其他地方使用可能會造成錯誤
### **<font color="B1D6CA">continue</font>**
continue 通常用於迴圈中,在其他地方使用可能會造成編譯錯誤,執行 continue 時 C++ 會馬上跳回迴圈的開頭然後再繼續執行,而不執行在 continue 後的程式碼
<font color="F5F6B6">**範例 1**</font>
```cpp=
#include <iostream>
using namespace std;
int main(){
int i=0;
while(true){
if(i<37){
cout<<i<<" onoq\n";
i++;
continue;
cout<<"no cout QQ\n";
}
else if(i==37){
cout<<i<<" !!! ouob\n";
i++;
break;
}
}
return 0;
}
```

## **<font color="9CCEF2">常用函式</font>**
C++ 中的函式庫,存有不少好用的函式,善加利用可以節省不少時間,但還是要先明白該如何撰寫再使用
### **<font color="B1D6CA">最大值、最小值</font>**
先試著撰寫一段能輸出最小值的程式碼吧
<font color="F5F6B6">**最小值**</font>
先輸入 n,再輸入 n 個數,輸出其中的最小值

```cpp=
#include <iostream>
using namespace std;
int main(){
int n,num;
cin >> n >> num;
int mi=num;
n-=1;
while(n--){
cin >> num;
if(num < mi){
mi=num;
}
}
cout << mi <<'\n';
return 0;
}
```
在 <font color="F5F6B6">**algorithm**</font> 中,最大值最小值的函式是 <font color="F5F6B6">**max(x,y)**</font> 跟 <font color="F5F6B6">**min(x,y)**</font>,用法如下
```cpp=
#include <iostream>
#include <algorithm>
using namespace std;
int main(){
int n,num;
cin >> n >> num;
int mi=num;
n-=1;
while(n--){
cin >> num;
mi = min(num,mi);
}
cout << mi <<'\n';
return 0;
}
```
:::info
一定要注意的是,x 和 y 的型別要相同,一方為 int 時另一方不可以是 long long 型態,也別忘了在上方要 #include \<algorithm>
在宣告最大值或最小值的變數時,要避免使用 max 和 min,因為這是函式的名稱,盡量不要重複使用,可用 ma、mi 或是其他名稱替代
:::
### **<font color="B1D6CA">最大公因數</font>**
輸入 a 和 b,輸出 a 跟 b 的最大公因數(ps. 運用輾轉相除法)

```cpp=
#include <iostream>
using namespace std;
int main(){
int a,b;
cin >> a >> b;
if(b>a){
int tmp=a;
a=b;
b=tmp;
}
while(b != 0){
a%=b;
int k=a;
a=b;
b=k;
}
cout << a <<'\n';
return 0;
}
```
最大公因數的函式 <font color="F5F6B6">**\_\_gcd(x,y)**</font> (gcd 前是兩個底線)在 \<algorithm> 中,同樣也要注意兩個變數的宣告型別要相同,否則程式會出錯
```cpp=
#include <iostream>
#include <algorithm>
using namespace std;
int main(){
int a,b;
cin >> a >> b;
cout << __gcd(a,b) <<'\n';
return 0;
}
```
### **<font color="B1D6CA">絕對值</font>**
輸入一個整數 n,輸出 n 的絕對值

```cpp=
#include <iostream>
using namespace std;
int main(){
int n;
cin >> n;
if(n < 0){
cout << -n <<'\n';
}
else{
cout << n << '\n';
}
return 0;
}
```
絕對值的函式 <font color="F5F6B6">**abs(x)**</font> 在 \<cmath> 中,然而 abs 預設值是 int,如果要輸出的值大於 int 會發生下圖情況

若變數型態是 long,就要使用 labs(n),但 cmath 中沒有 long long 型態的 abs,因此要引入函式庫 cstdlib 才能使用 llabs(n)
```cpp=
#include <iostream>
#include <cmath>
#include <cstdlib>
using namespace std;
int main(){
long long n;
cin >> n;
cout<<llabs(n)<<'\n';
return 0;
}
```

### **<font color="B1D6CA">交換兩數</font>**
輸入 a 和 b,將 a 與 b 的值交換後輸出。

```cpp=
#include <iostream>
using namespace std;
int main(){
int a,b,temp;
cin>>a>>b;
temp=a; //使用temp避免a的值被覆蓋掉
a=b;
b=temp;
cout<<"a="<<a<<" b="<<b;
return 0;
}
```
交換兩數的函式是 <font color="F5F6B6">**swap(a,b)**</font> ,也要注意兩個要被交換的變數型態必須相同。
```cpp=
#include <iostream>
using namespace std;
int main(){
int a,b;
cin>>a>>b;
swap(a,b);
cout<<"a="<<a<<" b="<<b;
return 0;
}
```
:::spoiler <font color="FEA0A0">**題目**</font>
1. [Zerojudge f312: 1. 人力分配](https://zerojudge.tw/ShowProblem?problemid=f312)
2. [Zerojudge a799: 正值國](https://zerojudge.tw/ShowProblem?problemid=a799)
3. [Zerojudge d065: 三人行必有我師](https://zerojudge.tw/ShowProblem?problemid=d065)
:::
---
## **<font color="9CCEF2">一維陣列</font>**
當遇到需要宣告同類型的多個變數時,如果一個個宣告變數會很麻煩,因此可以使用陣列(array)這個資料型態,來存取多個變數
<font color="F5F6B6">**一般宣告方式**</font>
```cpp=
int score0=90;
int score1=77;
int score2=85;
```
<font color="F5F6B6">**陣列**</font>
```cpp=
int score[3];
score[0]=90;
score[1]=77;
score[2]=85;
```
宣告一個長度為 3 叫做 score 的陣列,存有 3 個型態為 int 的變數,可以用迴圈來簡化
```cpp=
#include <iostream>
using namespace std;
int main(){
for(int i=0;i<3;i++){
cin>>score[i];
}
return 0;
}
```
宣告陣列的 `[]` 中表示陣列的長度,宣告的型別等於陣列中元素的型別,且陣列的編號是從零開始,編號皆為正整數
| 編號 | 數值 |
| ---- | ---- |
| 0 | 90 |
| 1 | 77 |
| 2 | 85 |
### **<font color="B1D6CA">陣列的更多細節</font>**
解題時,建議將陣列宣告在 main 函數的上方,以避免 [Stack Overflow](https://hackmd.io/tTAs-GXpQIqGLfIrEkfLRA) 的風險
```cpp=
#include <iostream>
using namespace std;
int score[3];
int main(){
for(int i=0;i<3;i++){
cin>>score[i];
}
for(int i=0;i<3;i++){
cout<<"score["<<i<<"]= "<<score[i]<<'\n';
}
return 0;
}
```
這樣宣告會讓 score 陣列成為一個 [**全域變數**](https://openhome.cc/Gossip/CGossip/Scope.html),因為若是宣告過大的陣列在 main() (包含其他函數)裡面,可能會在某些環境下導致程式當掉,宣告在全域變數就能避免掉這個問題
陣列一宣告完後大小就會固定不能改變,因此在宣告長度時要考慮到最糟糕的情況所需要的數量,並留至少 5~10 作為緩衝,降低溢位風險,題目如果沒有講明最大範圍的話都可以多宣告一些
<font color="F5F6B6">**陣列的初始值**</font>
```cpp=
int ary[3]={0,1,0};
```
:::info
{ } 僅限用於初始化,並不能用於對已宣告好的陣列做賦值(assign)的動作
:::
以下為錯誤示範,會導致編譯錯誤
```cpp=
int ary[3];
ary[3]={0,1,0}
```
初始化時沒有被指定到的部分會被自動填入 0,可以看下方的例子
```cpp=
#include <iostream>
using namespace std;
int ary[3]={0};
int main(){
for(int i=0;i<3;i++){
cout<<ary[i]<<' ';
}
return 0;
}
```

:::info
這段程式碼並不是將每一格都變成 0,而是第一位初始化成 0 後再補上 0
:::
所以下方的程式碼並不會因為第一位是 1,所以其他格都變為 1
```cpp=
#include <iostream>
using namespace std;
int ary[3]={1};
int main(){
for(int i=0;i<3;i++){
cout<<ary[i]<<' ';
}
return 0;
}
```

:::spoiler <font color="FEA0A0">**題目**</font>
1. [Zerojudge d660: 11764 - Jumping Mario](https://zerojudge.tw/ShowProblem?problemid=d660)
2. [Zerojudge b558: 求數列第 n 項](https://zerojudge.tw/ShowProblem?problemid=b558)
3. [Zerojudge a253: 王老先生的磨菇田](https://zerojudge.tw/ShowProblem?problemid=a253)
4. [Kattis Reduced ID Numbers](https://open.kattis.com/problems/reducedidnumbers)
5. [Zerojudge a240: 第一題:1 / 17 小數第 n 位](https://zerojudge.tw/ShowProblem?problemid=a240)
6. [CSES Missing Number](https://cses.fi/problemset/task/1083)
7. [Kattis Free Food](https://open.kattis.com/problems/freefood)
8. [Kattis Hot Hike](https://open.kattis.com/problems/hothike)
:::
>
:::spoiler <font color="FEA0A0">**題解**</font>
1. [Zerojudge d660: 11764 - Jumping Mario](https://zerojudge.tw/ShowProblem?problemid=d660)
```cpp=
#include <iostream>
using namespace std;
int wall[55];
int main(){
int t;
cin>>t;
int k=1; //計算第幾個 Case
while(t--){ //執行 t 次
int n;
cin>>n;
for(int i=0;i<n;i++){
cin>>wall[i]; //輸入每個牆壁高度
}
int up=0,down=0;
for(int i=0;i<n-1;i++){
if(wall[i]<wall[i+1]){ //計算往上跳的次數
up++;
}
if(wall[i]>wall[i+1]){ //計算往下跳的次數
down++;
}
}
cout<<"Case "<<k<<": "<<up<<' '<<down<<'\n';
k++; //輸出完是第幾個 Case 後,每輪再往上加 1
}
return 0;
}
```
:::
---
## **<font color="9CCEF2">二維、多維陣列</font>**
### **<font color="B1D6CA">二維陣列的宣告</font>**
```cpp=
int ary[n][m];
int num[2][4];
```
第一個 `[]` 表示<font color="F5F6B6">**列(row)**</font>,在第一維;第二個 `[]` 表示<font color="F5F6B6">**行(column)**</font>,在第二維。可以想像成是有 2 個長度為 4 的一維陣列,來儲存 2\*4 筆資料
| 列\行 | \[0\] | \[1\] | \[2\] | \[3\] |
| ------ | ----- | ----- | ----- | ----- |
| \[0\] | (0,0) | (0,1) | (0,2) | (0,3) |
| \[1\] | (1,0) | (1,1) | (1,2) | (1,3) |
表示位置 num\[1]\[3] 的值是 3:
```cpp=
num[1][3] = 3;
```
<font color="F5F6B6">**二維陣列的初始化**</font>
```cpp=
int num[2][4]={
{1,2,3,4},
{1,1,1,1}
}
```
二維陣列中要進行輸入輸出時,要一橫排處理完後在處理下一橫排,或是一直排處理完後在處理下一直排都可以,採第一種方式會**更有效率**,因為它和 C++ 實際儲存方式最相近,也和 cin、cout 的輸出輸入順序一致
```cpp=
for(int i = 0; i < r; i++){
for(int j = 0; j < c; j++){
cin>>ary[i][j];
}
}
```
### **<font color="B1D6CA">多維陣列</font>**
依照上述的邏輯,數個相同大小的二維陣列可以構成一個三維陣列,而數個相同大小的三維陣列可以構成四維陣列,以此類推
:::spoiler <font color="FEA0A0">**題目**</font>
1. [Zerojudge a015: 矩陣的翻轉](https://zerojudge.tw/ShowProblem?problemid=a015)
2. [Toj 114 / 我閉著眼](https://toj.tfcis.org/oj/pro/114/)
3. [Zerojudge d625: 踩地雷真好玩](https://zerojudge.tw/ShowProblem?problemid=d625)
4. [Zerojudge b965: 第 2 題 矩陣轉換](https://zerojudge.tw/ShowProblem?problemid=b965)
5. [UVA Q118 Mutant Flatworld Expolrers ](http://domen111.github.io/UVa-Easy-Viewer/?118)
6. [Kattis This Ain't Your Grandpa's Checkerboard](https://open.kattis.com/problems/thisaintyourgrandpascheckerboard)
:::
>
:::spoiler <font color="FEA0A0">**題解**</font>
2. [Toj 114 / 我閉著眼](https://toj.tfcis.org/oj/pro/114/)
```cpp=
#include <iostream>
using namespace std;
int ary[8][9];
int main(){
int i,j;
for(i=1;i<=5;i++){
for(j=1;j<=6;j++){
cin>>ary[i][j];
}
}
int k=0;
for(i=1;i<=5;i++){
for(j=1;j<=6;j++){
if(ary[i][j] == ary[i][j-1] && ary[i][j] == ary[i][j+1]){
k++;
}
else if(ary[i][j] == ary[i-1][j] && ary[i][j] == ary[i+1][j]){
k++;
}
}
}
if(k!=0) cout<<"Yes\n";
else cout<<"No\n";
return 0;
}
```
7. [Kattis Cudoviste](https://open.kattis.com/problems/cudoviste)
```cpp=
#include <bits/stdc++.h>
using namespace std;
char ary[60][60];
int ans[5];
int main(){
int r,c;
cin>>r>>c;
int i,j;
for(i=1; i<=r; i++){
for(j=1; j<=c; j++){
cin>>ary[i][j];
}
}
int b[5]={0,1,1,0,0};
int p=0;
for(i=1; i<r; i++){
for(j=1; j<c; j++){
for(int k=0;k<4;k++){
if(ary[i+b[k]][j+b[k+1]] == '#'){
p=-1;
break;
}
else if(ary[i+b[k]][j+b[k+1]] == 'X') p++;
}
if(p>=0) ans[p]++;
p=0;
}
}
for(i=0;i<5;i++){
cout<<ans[i]<<"\n";
}
return 0;
}
```
:::
---
## **<font color="9CCEF2">string 字串</font>**
### **<font color="B1D6CA">宣告與賦值</font>**
```cpp=
#include<string>
//string標頭檔,內含多種營養函式(?
string str1;
//宣告一個名為str1的string容器
//僅在c++可用,C語言未提供此型態
char str2[10];
//宣告一個名為str2的char陣列
//如果是使用C語言就只能用char陣列,沒法用string型態
str="Hello world!";
//把"Hello world!"指定給str
```
使用 string 型態時建議還是引入 **\<string>** 標頭檔,雖然現在不需要此標頭檔,也能宣告 string 及使用一些基本功能,不過一些功能還是要引入才能用。
(不過嚴格來說 string 好像不算是一種型態,更偏向是一種容器,有點像是 vector 那種,現階段就先暫時先把它當成一種形態就好)
### **<font color="B1D6CA">比較兩字串內容</font>**
如果要看內容是否相同,那直接用 `==` 就好了。 `==`
至於 ``>`` 跟 ``<`` 也是能用,比較順序是按照字典序來排列。
### **<font color="B1D6CA">字串串接</font>**
可以使用+來串接兩字串,或是使用 .append(str) 來處理。
建議用+比較方便。
```cpp=
A="Hello";
B="Hello";
A+=" world!";
//使用+來進行" world!"串接
B.append(" world!");
//或是使用.append()
//把" world!"串接到A與B後
cout<<A<<'\n'<<B;
```
輸出如下:
```
Hello world!
Hello world!
```
### **<font color="B1D6CA">取得字串長度</font>**
使用 ``.size()`` 或是 ``.length()`` 兩者,在 string 上不會有明顯差異。
需要注意的是別忘了後面的括號 ()。
```cpp=
str="Hello world!";
int S,L;
S=str.size();
//把str的大小使用.size()指定給S
L=str.length();
//把str的大小使用.length()指定給S
cout<<S<<' '<<L;
```
輸出如下:
```
12 12
```
### **<font color="B1D6CA">存取第n個字元</font>**
就像陣列一樣,可以直接以 str[n] 的方式處理,但存取超過陣列範圍的字元會出問題。
保險起見可以使用 ``.at(n)``,會有邊界檢查。如果超出範圍的話,雖然編譯時會給過,但執行時後面都不會進行,因為它會拋出一個 out_of_range 例外並終止程式。
```cpp=
str="Hello world!";
cout<<str[4]<<'\n';
cout<<str.at(4)<<'\n';
cout<<str.at(15)<<'\n';
//超出str範圍
//拋出exception :throws: out_of_range
cout<<str[4]<<'\n';
cout<<str.at(4)<<'\n';
```
輸出如下:
```
o
o
```
使用 ``.at()`` 的穩定性較高,但一般時候其實用 str[n] 就好了,效率會比較好。
### **<font color="B1D6CA">string 轉 int、int 轉 string</font>**
使用 `to_string(n)` 則可以將 n 轉為字串型態。
反過來說,`stoi(str)` 可以將 str 字串的內容轉為 int,而因為型態變為 int,開頭的 0 就會消失了
```cpp=
string str1="0324",str2;
int n1=135,n2;
n2=stoi(str1);
//將str1轉為int型態指定給n2
str2=to_string(n1);
//將n1轉為string型態指定給str2
cout<<str2<<' '<<n2;
```
輸出如下:
```
135 324
//str1原為"0324",轉為int後因為首位不會是0,所以變成324
```
### **<font color="B1D6CA">取子字串</font>**
使用 ``.substr(x,n)`` 來取得 x 開始的 n 個字組成的子字串。
```cpp=
str1="Hello world!";
cout<<str1.substr(6,5);
//從str1的第6個字開始取5個字元
```
輸出如下:
```
world
```
### **<font color="B1D6CA">插入、取代與刪除字串</font>**
使用 ``.insert(x,str)`` 在指定位置 x 插入 str。
取代則是用 ``.replace(x,n,str)``,把 x 開始的 n 個字元替換成 str。
刪除某段就用 ``.erase(x,n)``,把從 x 開始的 n 個字元刪掉。
切記,string 跟陣列一樣,第一個字元的位置是 0。
```cpp=
str="Hello world!";
str.insert(5," John");
//Hello John world!
cout<<str<<'\n';
str.replace(6,4,"Maria");
//Hello Maria world!
cout<<str<<'\n';
str.erase(5,6);
//Hello world!
cout<<str;
```
輸出如下:
```
Hello John world!
Hello Maria world!
Hello world!
```
### **<font color="B1D6CA">尋找字串中指定字串</font>**
使用 ``.find(str,n)`` 來尋找指定字串,回傳值為從n開始,找到的第一個str的開頭位置。
如果沒找到的話,則會回傳 string::npos。
```cpp=
str="Hello world Hello";
if( str.find("Hello",0) != string::npos ){
//find("Hello",0) 不等於 string::npos
//找到第一個"Hello"在第0個位置
cout<<str.find("Hello",0)<<'\n';
}
else cout<<"Not found!";
if( str.find("PPAP",0) != string::npos ){
cout<<str.find("PPAP",0)<<'\n';
}
else cout<<"Not found!";
//find("PPAP",0) 等於 string::npos
//也就是找不到"PPAP"
```
輸出如下:
```
0
Not found!
```
### **<font color="B1D6CA">反轉字串</font>**
這個是搭配 `reverse(a,b)` 函式來使用的,比較特別的是 a 跟 b 要傳入的是<font color="F5F6B6">**「位置」**</font>。
如果要從 str 的開頭到結尾反轉,那 a 就是 `str.begin()`,代表 str 開頭的那個位置,而 b 則是 `str.end()`,代表 str 的結尾位置。
而如果是從第二個字元開始,那就把 a 換成 `str.begin()+1`。如果只轉到倒數第二個,那就把 b 換成 `str.end()-1`,簡而言之,往後一個就 +1,往前一個就 -1。
如果要使用 `reverse()`,記得要引入 \<algorithm> 標頭檔。
```cpp=
#include<algorithm> //包含了reverse()在內
.
.
.
str="Hello world!";
reverse(str.begin(),str.end());
//由str的第一個至最後一個反轉
cout<<str<<'\n';
```
輸出如下:
```
!dlrow olleH
```
### **<font color="B1D6CA">getline</font>**
一般使用 cin 輸入字串時,每遇到空格或換行就會被判斷是下一個輸入,如果需要輸入一個文章,或是一段完整句子 cin 就無法輸入完全,因此會需要用到 getline
```cpp=
string s;
getline(cin,s);
cout<<s<<'\n';
```

使用 getline 輸入不會被空格截斷,每遇到換行會先接收換行的指令再跳下一個輸入,可以看下面的例子
<font color="F5F6B6">**範例 1**</font>
```cpp=
cin>>a;
getline(cin,str);
cout<<a<<'\n'<<str;
```

在這個例子中,我們輸入了 a 以後,還沒輸入字串程式就結束了
這是因為 cin 完最後的位子在 3 的後一格,所以 getline 讀到的是換行,而 getline 遇到換行會停下,所以要輸入一段字串的話,要使用兩次 getline(或是 gets()/getchar()) 第一個吃掉換行,第二格才是輸入你要輸入的字串
```cpp=
cin>>a;
getline();
getline(cin,str);
cout<<a<<'\n'<<str;
```

:::spoiler <font color="FEA0A0">**題目**</font>
1. [Zerojudge a442: Necklace Problem (NP)](https://zerojudge.tw/ShowProblem?problemid=a442)
2. [Toj 92 / 天線寶寶說你好](https://toj.tfcis.org/oj/pro/92/)
3. [Zerojudge d535: 2. 密碼驗證與擷取](https://zerojudge.tw/ShowProblem?problemid=d535)
4. [Zerojudge a038: 數字翻轉](https://zerojudge.tw/ShowProblem?problemid=a038)
5. [Zerojudge e267: 11192 - Group Reverse](https://zerojudge.tw/ShowProblem?problemid=e267)
6. [Kattis Quick Estimates](https://open.kattis.com/problems/quickestimate)
7. [Toj Reversed card open!](https://toj.tfcis.org/oj/pro/115/)
8. [Kattis Line Them Up](https://open.kattis.com/problems/lineup)
9. [Toj Hello world!](https://toj.tfcis.org/oj/pro/5/)
10. [Kattis Ragged Right](https://open.kattis.com/problems/raggedright)
11. [Toj J. Julius Caesar](https://toj.tfcis.org/oj/pro/127/)
12. [Toj 跳躍](https://toj.tfcis.org/oj/pro/113/)
13. [Kattis The Key to Cryptography](https://open.kattis.com/problems/keytocrypto)
14. [UVA Q11577 Letter Frequency](http://domen111.github.io/UVa-Easy-Viewer/?11577)
15. [Kattis Trik](https://open.kattis.com/problems/trik)
16. [Toj 4. Hello, TFcis!](https://toj.tfcis.org/oj/pro/294/)
17. [Zerojudge a011: 00494 - Kindergarten Counting Game](https://zerojudge.tw/ShowProblem?problemid=a011)
:::
>
:::spoiler <font color="FEA0A0">**題解**</font>
3. [Zerojudge d535: 2. 密碼驗證與擷取](https://zerojudge.tw/ShowProblem?problemid=d535)
函數解:
```cpp=
#include <iostream>
#include <algorithm>
#include <string>
using namespace std;
int main(){
string s;
cin>>s;
string ss=s;
reverse(s.begin(),s.end());
if(ss == s){
string ans="";
int a,b;
bool add=false;
for(int i=1;i<ss.size();i++){
a=ss[i-1]-'0';
b=ss[i]-'0';
if(b > a*2){
cout<<"INCORRECT\n";
return 0;
}
if(a%2 == 0){
ans+=ss[i-1];
add=true;
}
}
if(b%2 == 0){
ans+=char(b+'0');
}
else if(!add) cout<<"0";
cout<<ans<<'\n';
}
else cout<<"INCORRECT\n";
return 0;
}
```
非函數解
```cpp=
#include <iostream>
#include <algorithm>
#include <string>
using namespace std;
int main(){
string s;
cin>>s;
int len=s.size();
for(int i=0;i<len/2;i++){
if(s[i] != s[len-i-1]){
cout<<"INCORRECT\n";
return 0;
}
}
string ans="";
for(int i=0;i<len-1;i++){
if(s[i+1]-s[i] > s[i]-'0'){
cout<<"INCORRECT\n";
return 0;
}
if(int(s[i]-'0')%2 == 0) ans+=s[i];
}
if(int(s[len-1]-'0')%2 == 0) ans+=s[len-1];
if(ans.size() == 0) cout<<'0';
cout<<ans<<'\n';
return 0;
}
```
:::