--- 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++ 中,點擊檔案 -> 開新檔案 -> 原始碼 / 檔案下方空白圖示 ![](https://i.imgur.com/X7Dpv8S.png) -> 原始碼 / 快捷鍵(CTRL+N) ,即可開啟一個空白的檔案,按下 F12 或是 ![](https://i.imgur.com/VbfN9Lm.png) 就能進行編譯 ### **<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; } ``` ![](https://i.imgur.com/lyVkWpv.png) 透過 `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; } ``` ![](https://i.imgur.com/H5dxh9L.png) 運用上面講過的 `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; } ``` ![](https://i.imgur.com/BEkHKPt.png) 在程式中,和平常進行運算時一樣,都是先乘除後加減,想先計算加減時,可用 `()` 包起來,最內層的 `()` 會先進行運算 <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; } ``` ![](https://i.imgur.com/0C5s0ak.png) 也可讓一個變數的值 = 另一個變數的值(將一個變數的值賦予給另一個變數),甚至做運算 ```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; } ``` ![](https://i.imgur.com/WVCfAuh.png) 表示 `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** 號 -> 小寫字母 - 其他為標點符號、運算符號等 ![](https://cdn.discordapp.com/attachments/791701979704983554/889488133970862130/Ascii.jpg) 運用以下程式碼即可知道 **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; } ``` ![](https://i.imgur.com/EsEXcV6.png) 也可從 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; } ``` ![](https://i.imgur.com/d3ZbGuw.png) :::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; } ``` ![](https://i.imgur.com/oSIO1a6.png) ![](https://i.imgur.com/VqDbi4R.png) <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; } ``` ![](https://i.imgur.com/R125agS.png) ![](https://i.imgur.com/nc2sI9t.png) 當 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; } ``` ![](https://i.imgur.com/363xY5R.png) ![](https://i.imgur.com/72zmtP1.png) 邏輯運算子可以一次使用多個,會先執行 `&&` 再執行 `||`,使用 `()` 就能先執行 `()` 中的指令 除了 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; } ``` ![](https://i.imgur.com/DXz85aP.png) :::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; } ``` ![](https://i.imgur.com/HQVruTP.png) 使用 <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; } ``` ![](https://i.imgur.com/s9tFmaf.png) 因為想要執行重複輸出這個動作,所以使用迴圈並設定一個 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; } ``` ![](https://i.imgur.com/TY48gHf.png) **在不知道執行次數,但知道何種條件下要終止迴圈時,可以使用 <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; } ``` ![](https://i.imgur.com/cXcNccw.png) <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; } ``` ![](https://i.imgur.com/ZXWdg4P.png) :::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; } ``` ![](https://i.imgur.com/iR24vbb.png) 這段程式碼表示,一開始先宣告一個 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; } ``` ![](https://i.imgur.com/0ndDnPK.png) ![](https://i.imgur.com/eFLRssf.png) 因為我們要找的是 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; } ``` ![](https://i.imgur.com/dolr5Rt.png) 在同個段落中,要執行多件事可以用逗號分割 :::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 的正方形吧 ![](https://i.imgur.com/ejeJUeW.png) 想輸出這樣的矩形,可以分成兩部分,輸出 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 的直角三角形吧 ![](https://i.imgur.com/pinTZzx.png) 透過觀察,我們可以知道三角形在第一層時輸出一個 * 第二層兩個,以此類推,這次我們先建好一個高,長為 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 的倒直角三角形 ![](https://i.imgur.com/uWtce8P.png) ```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 的等腰三角形 ![](https://i.imgur.com/lFrH3JJ.png) ```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 的平行四邊形 ![](https://i.imgur.com/wNVICOi.png) 透過這張圖,我們可以分析出,它是由一個倒直角三角形加上一個正方形所組成的 ![](https://i.imgur.com/rKLbElT.png) 運用輸出正方形的程式碼(在練習時請自己重新打一個,不要用複製貼上的方式,否則無法增加熟練度) ```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 的正三角形 ![](https://i.imgur.com/vA7Segp.png) 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; } ``` ![](https://i.imgur.com/9nwzrG1.png) <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 撰寫時要記得,... 的左右都需要加上空白 ::: ![](https://i.imgur.com/DQNOcRc.png) 除此之外,`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; } ``` ![](https://i.imgur.com/pnX0liX.png) 只要迴圈是 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; } ``` ![](https://i.imgur.com/9krGVvd.png) ## **<font color="9CCEF2">常用函式</font>** C++ 中的函式庫,存有不少好用的函式,善加利用可以節省不少時間,但還是要先明白該如何撰寫再使用 ### **<font color="B1D6CA">最大值、最小值</font>** 先試著撰寫一段能輸出最小值的程式碼吧 <font color="F5F6B6">**最小值**</font> 先輸入 n,再輸入 n 個數,輸出其中的最小值 ![](https://i.imgur.com/E9AhBTY.png) ```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. 運用輾轉相除法) ![](https://i.imgur.com/swYFXHi.png) ```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 的絕對值 ![](https://i.imgur.com/IhrhFdO.png) ```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 會發生下圖情況 ![](https://i.imgur.com/zwAiZo2.png) 若變數型態是 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; } ``` ![](https://i.imgur.com/IxqFERL.png) ### **<font color="B1D6CA">交換兩數</font>** 輸入 a 和 b,將 a 與 b 的值交換後輸出。 ![](https://i.imgur.com/xNNqh39.png =590x) ```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; } ``` ![](https://i.imgur.com/1Ovj3K4.png) :::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; } ``` ![](https://i.imgur.com/jrBQaBf.png) :::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'; ``` ![](https://i.imgur.com/3Rvavsj.png) 使用 getline 輸入不會被空格截斷,每遇到換行會先接收換行的指令再跳下一個輸入,可以看下面的例子 <font color="F5F6B6">**範例 1**</font> ```cpp= cin>>a; getline(cin,str); cout<<a<<'\n'<<str; ``` ![](https://i.imgur.com/kYlyDi5.png) 在這個例子中,我們輸入了 a 以後,還沒輸入字串程式就結束了 這是因為 cin 完最後的位子在 3 的後一格,所以 getline 讀到的是換行,而 getline 遇到換行會停下,所以要輸入一段字串的話,要使用兩次 getline(或是 gets()/getchar()) 第一個吃掉換行,第二格才是輸入你要輸入的字串 ```cpp= cin>>a; getline(); getline(cin,str); cout<<a<<'\n'<<str; ``` ![](https://i.imgur.com/6zN8KAP.png) :::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; } ``` :::