No code no life

2020 暑假資訊讀書會

暑假資讀簡介

  • 對象:027
  • 時間:8/10-8/12 上午9:00-12:30
  • 地點:資源大樓三樓 電腦教室一
  • 講師名單:123賴昭勳 127黃思維 127張均豪
  • 授課內容:C++基礎語法、資訊競賽簡介

Codeblocks 下載

第一支程式

#include <iostream>
using namespace std;
int main() {
	cout << "Hello World!" << endl;
}

C++ 程式的組成

  • 用來使用某些特定的內建功能,引入內建的函式庫。
  • 基本上寫在最前面
  • 處理C++式輸入輸出:#include <iostream>

1. 標頭檔

  • 程式裡面的一組物件、類別、函式加上的標籤(為了避免名稱相撞)
  • 標準函式庫:using namespace std

 許多功能原本需要加上 std::function_name

using 就是直接告訴編譯器:「跟std名字一樣的東西就是std的」

2. 命名空間

  • C++ 程式執行時會自動跑的函式
  • int main() {}
回傳值:給編譯器看

名字跟參數

程式碼放這

3. 主函式

\注意/

#include <iostream>
using namespace std;
int main() {

}
#include <iostream>
using namespace std;
int main() 
{

}

唯一真理

邪教

自己試試看吧!

變數、型別、運算子

C++  如何儲存資料

不是01010111嗎?

 

雖然電腦裡所有資料都是二進位,程式語言會為使用者做好資料型別(data types),以方便處理不同種類的資料.

名稱 大小(位元組) 數值範圍
int 4 -2147483648~2147483647
unsigned int 4 0~4294967295
long long 8 -2^63~2^63 - 1
char  1 -128~127 (或0~255)
float 4 3.4E +/- 38 (7 位數)
double 8 1.7E +/- 308 (15 位數)
long double 8 (?) 1.7E +/- 308 (15 位數)
bool 1 true 或 false

變數

儲存資料的地方

int a;

型別

名稱

可以想成「記憶體中的某個放東西的位置」

有了變數可以做什麼?

運算子(operator)

  • 對資料進行某種操作和改變
  • 每個型別能用的運算子和功能不太一樣

int 的運算子

名稱 功能
賦值(=) 將左邊的變數數值改成右邊的
加(+)
減(-)
乘(*)
除(/) 會自動取下高斯喔
模運算(%) a % b 為 a 除 b 的餘數

扣的

int a = 2, b = 0;
a = a + 5 * 6 / 7 % 3;
b += a;
a += b;
cout << a << " " << b <<  endl;

現在 a, b 是多少呢?

 char 的運算

其實跟int 差不多?

C++ 的字元是從ASCII 編碼實作,因此每個字元對應到一個數字(中文沒有喔)。

char c = 'a';
cout << c + 1 << endl; //b

bool 的概念

bool 是布林值(boolean)的縮寫,其實就只是true 跟 false 的狀態,常用來判斷條件等。

bool 運算子

名稱 簡介 舉例
and (&&) 左右都是true true && true == true,
false && true == false
or (||) 其中一個true true || false = true,
false || false = false
not (!) 反過來 !true = false, !false = true
xor (^) 左右不相同 true ^ false = true,
true ^ true = false

基本輸入輸出

選擇結構(if/else)

要怎麼做判斷?

if (條件成立) {
    //執行程式
} else if (上面不成立時下面成立) {
    //...
} else {
    //前面都不符合時
}

這三個都可以自由拼湊喔(除了if 一定要寫XD)

條件式

裡面其實是一個布林值!C++會判斷裡面的東西是不是 true。

==

(不是表情符號)

判斷左右兩個東西是否相同。是的話回傳true, 否則回傳false.

int a = 5, b = 5;
if (a == b) {
    cout << "7122" << endl;
}

其他關係運算子

名稱 功能 舉例
!= 判斷兩者是否不同 5 != 8
< 左<右 7 < 9
<= 左<=右 -5 <= -5
> 6 > 4
>= 8 >= 8

實作time!

Q1: 輸入兩個數字a, b,判斷a是不是b的整數倍。

Q2: 輸入兩個年齡a, b,如果有一者大於等於18而另一個小於18、或兩者小於16,輸出"FBI OPEN UP"。否則輸出"LEGAL"

 

(這題敘...)

迴圈

有一些東西要重複很多次,甚至不知道要做幾次,怎麼辦?

用迴圈!!!

while 迴圈

while (條件成立) {
    //做事
}

飯粒

int a = 5;
while (a > 0) {
	cout << a << endl;
    a = a - 1;
}

for 迴圈

有點像是while 的進階版!

不覺得剛剛的code 可以再簡單點嗎?

與髮

for (起始條件; 判斷條件; 更新動作) {
	//做事
}

舉個例子吧!

for (int i = 0;i < 10;i++) {
	cout << i * i << endl;
}

for 迴圈疊for 迴圈...

大家要記得,上面寫的這些東西都可以重複堆疊喔!

for (int i = 0;i < 10;i++) {
	for (int j = 0;j < 10;j++) {
    	cout << i * 10 + j << endl;
    }
}

如果要讓東西重複n次呢?

 

int n;
cin >>n;
for (int i = 0;i < n;i++) {

}

現在,你已經會寫基本的程式了!

來練習吧!

Q1: 讓使用者輸入一個數字,程式輸出該數字四捨五入至十位數的結果。

ex. 輸入 7122, 輸出 7120
輸入 8, 輸出 10

Q2: 輸入一個整數,判斷該數字是不是質數。如果是的輸出 YES, 否則輸出 NO

Q3: 輸入一個整數 n,輸出1~n 的所有質數,並在最後輸出總共有幾個。

 

P.S. 這題注意一下執行效率喔!(n <= 100000)

Q4: 輸入兩個整數,輸出他們的最大公因數和最小公倍數。

一些第二位學長喜歡

但是不在他範圍講不到的東西

還很毒

Visual Studio Code

毒瘤

#include <bits/stdc++.h> 

這個蠻好用的w

#if 0

#endif

這什麼鬼==

//*

//*/

int foo{} ;

殺蛇

#define

scope

Switch

不是遊戲機

陣列

以下講者腦補

某學弟:

我想要統計誰會來陽光宅男杯。

#include <bits/stdc++.h>
int main(){
    bool num1 ;
    bool num2 ;
    bool num3 ;
    bool num4 ;
    bool num5 ;
}

好累喔

程式真爛,這麼麻煩

C++之神:

我聽到你的心聲了

 

#include <bits/stdc++.h>
int main(){
    bool num[31] ;
}

學弟:好,我知道你很神了

但是怎麼用

像這樣,一個變數當火車頭,後面跟著車廂

好,那怎麼去到車廂?

#include <bits/stdc++.h>
int main(){
    bool num[31] ;
    num[3] = true ;
}

注意!火車頭自己也是一節車廂

#include <bits/stdc++.h>
int main(){
    bool num[31] ;
    num[0] = true ;
}

數字代表你要去火車頭"後"第幾節車廂

注意!!!

C++規定每一節車廂類型都要一樣(方便管理)

Python的車廂可以不一樣,所以管理不易

後來,學弟有了新夢想:

我要幫全校統計新生訓練有誰會來!!

但是學弟不知道每一班分別有幾人,於是

#include <bits/stdc++.h>
int main(){
    int amount ;
    std::cin >> amount ;
    bool num[amount] ;
}

萬萬不可!!!

回到火車的例子

火車只能在發車時改變車廂數目

如果中途改變會怎樣? 很亂!!

看看隔壁的C

學弟:但是我真的很想

其實你直接照上面那樣寫不會出錯,因為編譯器知道這世界上就是有這種懶人,所以偷偷讓你用

但是我覺得不好,因為有些編譯器沒那麼貼心。

解方:Vector!!

#include <bits/stdc++.h>
int main(){
    int amount ;
    std::cin >> amount ;
    std::vector<bool> num ;
    num.reserve(amount) ;
}

Vector是什麼?

vector是一位工人,告訴你貨物在哪個火車上,

順便幫你搬貨物

 

 

火車滿了裝不下,怎麼辦?

再找一輛更長的火車,把貨物搬到新的火車上,完成!

而且這個工人很聰明,會幫你自動判斷。

那上面的num.reserve()在幹嘛

我們先告訴工人我們要的火車有多長

這樣他就不用把貨搬來搬去

比較省時間

實作

我戳題技巧很爛

陣列

陣列

陣列

陣列

陣列

陣列

學弟:C++好爛,Excel比它好用多了

Excel兩個方向都有格子,C++的陣列只有1個方向,爛死了

陣列的真諦

你知道陣列的每一格也可以是陣列嗎

要包幾層就包幾層!!!

還敢說Excel好呀?

那要怎麼包?

#include <bits/stdc++.h>
int main(){
    int a_list[5][7] ; //我包2層
    int b_list[7][12][2] ; //我包3層
}
// 記得,陣列的每一格都要長一樣!

到底是誰包誰?

int b_list[7][12][2] ;

b_list有7格,每一格又有12小格,一小格又有2區

實作

Q2 寫一個2層vector的結構

函數

殘酷的事實

所有能用函數完成的東西,用迴圈也可以

那我學函數幹嘛

方便,簡潔,你以後會明白

學弟:我想知道任意數字的倒數是多少

#include <bits/stdc++.h>
int main(){
    float a ;
    std::cin >> a ;
    std::cout << 1.0/a ; 
}

另一位學弟:哇! 這程式好棒,我要放在我的程式裡! 但是我應該要複製哪一塊?

聰明的學弟把他的程式變成了函式

float rev(float x){
    return 1.0/x ;
}

於是另一位學弟完成了\( \frac 1 {\frac 1 a + \frac 1 b} \) 的程式

#include<bits/stdc++.h>
float rev(float x){
    return 1.0/x ;
}
int main(){
    float a,b ;
    std::cin >> a >> b ;
    std::cout << rev(rev(a)+rev(b)) ;
}

那不用函式會怎樣?

#include<bits/stdc++.h>
int main(){
    float a,b
    std::cin >> a >> b ;
    cout << 1.0/(1.0/a+1.0/b) ;
}

好像反而更簡潔?

先別管那個了,讓我教你怎麼寫函式

//↓ 每個函式都要告訴電腦,他會傳哪種資料回來
int main(/* 函式應該知道的東西,叫參數 */){
    //函式的內容
    return 0 ;
    //每個函式最後都要return
}
//example
bool big(int a, int b){
    if (a > b) return true ;
    else return false ;
}

大發現! int main(){}竟然也是函式

所以其實你早就會函式了喔!

怎麼用?

#include <bits/stdc++.h>
bool big(int a, int b){
    if (a > b) return true ;
    else return false ;
}
int main(){
    int x = 5, y = 7 ;
    cout << big(x,y) ; //false 
    //就這麼簡單!
}

實作

老實說 我覺得沒有什麼好做的

Q1 寫一個"上高斯" 函數

Q2 寫一個可以在指定陣列的最尾端加值的函數

多型與重載

毒瘤,有時間再講

遞迴

學弟:我要計算費氏數列!

0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610, 987, 1597, 2584, 4181, 6765, 10946, 17711, 28657, 46368, 75025, 121393, 196418, 317811, 514229, 832040, 1346269, 2178309, 3524578, 5702887, 9227465, 14930352, 24157817, 39088169, 63245986, 102334155

(oeis A000045)

 

好難算喔,請電腦幫幫幫我!!

只要寫一個函式,可以執行

\( F_n = F_{n-1} + F_{n-2} \) 就好了

int f(int index){
	if (index == 0 || index == 1) return 1 ;
    return f(index-1)+f(index-2) ;
}

這就是遞迴

當然,迴圈也做得到

而且這方法更快

int f[100]{1,1} ;
for(int i = 2 ; i <= n ; i++){
    f[i] = f[i-1] + f[i-2] ;
}

Why?

方法1

方法2

via http://mshang.ca/syntree/

同樣的東西 盡量避免重複算

實作

Q1 費氏數列 n<=50

hint: 拿前一項x0.618

Q3 費氏數列 n < 1e6 模998244353

我不會用ojdl 我就爛

字串

字元

就是一個字元

char c='a'
//字元以單引號' '夾住

ASCII Code

每個字元都有一個數字編碼

其他語言也有自己的編碼如:中文的big-5,unicode

字串

C字串(字元陣列)

很多個字元組成的陣列

對陣列的操作都可以適用

但我沒有要講這個

char str[20]="Hello, world!";
//字串用雙引號" "夾住
cout<<str[4];
// o

string

需引入標頭檔<cstring> (萬用標頭檔也有)

算是vector的特化,功能跟vector有點像

也跟vector一樣可變長度

#include<cstring>
string s="Hello, world!";
cout<<s[4];
// o

字串的讀入

看到換行或空白就會停止

若想要一次讀一行,可使用getline

string s;
cin>>s;
cout<<s<<endl;
// input: Hello, world!
// output: Hello,

getline(cin,s);
cout<<s<<endl;
// input: Hello, world!
// output: Hello, world!

字串的操作

賦值: =

合併: +

比較: == 、 > 、 <

比較的東西: 字典序

只有string可以用這些運算子,字元陣列不行

有趣的東西

stringstream

功能類似將一整行讀進來後,再以空白分開(?

在某些輸入格式很毒瘤的題目或許會有奇效

int main(){
    string s,tmp;
    getline(cin,s);
    stringstream ss(s);
    while(ss>>tmp){
        cout<<tmp<<endl;
    }
    return 0;
}

練習:字串反轉

練習:凱薩加密

指標

當我們需要交換兩個變數...

void swap(int x,int y){
    int tmp=x;
    x=y;
    y=tmp;
}
int main(){
    int x,y;
    cin>>x>>y;
    swap(x,y);
    cout<<x<<" "<<y<<endl;
}

有什麼問題?

當我們需要交換兩個變數...

呼叫函數時,程式實際做的事是把x和y複製一份,然後交換

主程式中的x和y,其實沒有真正交換到

指標:儲存記憶體位址

int x = 3; //一個整數變數
int *p = &x;
// int *p: 整數型別的指標
// &x: 取x變數的記憶體位址
int* p; //也可以這樣
int *a, *b; //a,b都是指標
int* a, b; //只有a是指標

取址與取值

int x = 3; //一個整數變數
int *p = &x;
// int *p: 整數型別的指標
// &x: 取x變數的記憶體位址
cout<<(*p)<<endl;
// *p: 該記憶體位址儲存的值
// (其實就是x)

回到剛剛的問題

void swap(int *x,int *y){
    int tmp=*x;
    *x=*y;
    *y=tmp;
}
int main(){
    int x,y;
    cin>>x>>y;
    swap(&x,&y);
    cout<<x<<" "<<y<<endl;
}

要這樣寫

或是這樣

void swap(int &x,int &y){
    int tmp=x;
    x=y;
    y=tmp;
}
int main(){
    int x,y;
    cin>>x>>y;
    swap(x,y);
    cout<<x<<" "<<y<<endl;
}

int &x: 參照

可以想像成他就是把東西丟進去了

好麻煩?

使用全域變數

不要自己寫swap

 指標的一些用途

當函數傳入引數

當陣列索引值

void print_arr(int* x,int cnt){
    for(int i=0;i<cnt;i++){
    	cout<<*(x+i)<<endl;
    }
}

 指標的一些用途

動態宣告記憶體(new)

把一串東西用指標串起來

之後可能會講到

struct node{
    int val;
    node* next;
    node(int v){
        val=v;
    }
};
node *root, *nd = new node(7122);
root->next = nd;

自定義資料型別

就是struct啦

然後struct和class有哪些不同

當一個東西有不同屬性

而你想把他們綁在一起時:

struct score{
    int math;
    int eng;
    bool pretend_weak;
};

當一個東西有不同屬性

存取屬性就這樣寫

struct score{
    int math;
    int eng;
    bool pretend_weak;
};
int main(){
    score myscore;
    myscore.math=71;
    myscore.eng=22;
}

關於他的一些操作

可以包函式

struct score{
    int math;
    int eng;
    bool pretend_weak;
    score(int m,int e){
        math=m;
        eng=e;
        if(math<60||eng<60){
            pretend_weak=true;
        }
        else{
            pretend_weak=false;
        }
    }
};
int main(){
    score myscore=score(87,78);
    score yourscore=score(71,22);
    cout<<myscore.pretend_weak<<endl; //false
    cout<<yourscore.pretend_weak<<endl; //true
}

一些應用

指標線段樹!

當你宣告一個變數時,你不想直接存取他,而是透過指標指向他的方式存取的話,就可以用new動態宣告變數

注意:如果本身是指標的話,存取屬性要用 ->

struct node{
    int val;
    node* next;
    node(int v){
        val=v;
    }
};
node *root, *nd = new node(7122);
root->next = nd;
cout<<root->next->val<<endl; //7122

資訊競賽簡介

先來玩玩吧

重要比賽們

校隊初選

北市賽

全國賽(前十進選訓)

TOI初選

選訓

IOI

次要比賽們

HP codewars

NPSC

YTP

ISSC(?

Google code jam

......

Online Judge 們

是什麼應該不用多講(?

zerojudge

greenjudge

UVa

codeforces

TIOJ

ojdl

.......

正式資訊讀書會

  • 對象:全體高一生(有人要幫忙宣傳嗎@@
  • 時間:每周二下午6:00-9:00
  • 地點:資源大樓三樓 電腦教室三
  • 授課內容:演算法競賽相關
Made with Slides.com