# 112 北科 程設 參考詳解
> ==採用 [CC BY-NC-SA 4.0](https://creativecommons.org/licenses/by-nc-sa/4.0/deed.zh-hant) 許可協議共享,轉載請註明來源,禁止任何營利使用。==
> [name=SayA] [time=Tue, Feb 20, 2024 10:19 PM] [color=#4c33af]
- [PDF手寫檔](https://drive.google.com/file/d/1IapZhbJZ0psx16S5QA64RZz7XLUfK1XV/view?usp=sharing),手寫檔會用螢光筆標註一些重要的地方,可搭配服用。
- 程式碼部分修改自 [@jack804916](https://www.dcard.tw/f/graduate_school/p/254653623) 提供的程式碼,使得我不用花過多精力在OCR考卷上,在此致謝。
- 修改後的程式碼與考卷上的**行號對齊**。
## Problem 1
### 題目
- Suppose that the outputs of the following `C` program are as follows: $2, 3, 12, 21, 23, 43, 65$
- Please trace the program and fill the blanks with correct statements.
### 答案
1. `NULL`
2. `parent->left`
3. `parent->right`
4. `root`
5. `root->left`
6. `root->right`
### Code
```cpp=
#include <stdio.h>
#include <stdlib.h>
typedef struct node {
int data;
struct node *left;
struct node *right;
} node_t;
typedef node_t * nodep_t; // a nodep_t is a pointer to a node_t
nodep_t createNode(int value) {
nodep_t p = (nodep_t) malloc(sizeof(node_t));
p->data = value;
p->left = p->right = NULL; // 左右子節點初始化為空節點 /* Problem 1-1 */
return p;
}
void build(nodep_t current, nodep_t newNode, int value) { // 從二分可以判斷是BST
nodep_t parent;
while (1) {
parent = current;
if (value < parent->data) {
current = current->left;
if (current == NULL) {
parent->left = newNode; /* Problem 1-2 */
return;
}
}
else {
current = current->right;
if (current == NULL) {
parent->right = newNode; /* Problem 1-3 */
return;
}
}
}
}
nodep_t insert(int value, nodep_t root) {
nodep_t current, parent, newNode;
newNode = createNode(value);
if (root == NULL) {
root = newNode;
} else {
parent = current = root; // parent其實不會用到,是誤導用的,考慮cur即可 /* Problem 1-4 */
build(current, newNode, value);
}
return root;
}
void inorder_traversal(nodep_t root) {
if (root != NULL) {
inorder_traversal(root->left); /* Problem 1-5 */
printf("%d ", root->data);
inorder_traversal(root->right); /* Problem 1-6 */
}
}
int main() {
nodep_t root = NULL;
int array[] = {23,12,43,21,65,3,2};
for (int i=0; i<7; i++) {
root = insert(array[i], root); // 由題幹中可知inorder traversal的結果是由小到大的,所以是BST
}
inorder_traversal(root);
return 0;
}
```
### 說明
- 從給定的輸入是 $23, 12, 43, 21, 65, 3, 2$ 和 `inorer traversal` 的結果是 $2, 3, 12, 21, 23, 43, 65$ ,可以得出是BST,但直接trace code就可了。
- `build` 為根據二分結果插入newNode,也可以得出為BST的insert
- ++line 43++ 的 $parent$ 其實沒用到,是誤導用的。
## Problem 2
### 題目
Please trace the following `C` program and answer the output of each printf() statement for problems 2-1~2-6.
### 答案
1. `NTUTGood`
2. `0`
3. `5 7`
4. `21`
5. `4.00`
6. `001` 假設array D被初始化為0,否則未初始化的變數具有不確定的值,可能會導致 ==Undefined behavior (UB)==。
### Code
```cpp=
#include <stdio.h>
#include <stdlib.h>
typedef enum {black_ops, top_secret, secret, non_secret} securitylevels; // 0, 1, 2, 3
void f1(char *s1, char *s2) { // s1 = concatenation of s1 and s2
for (; *s1!='\0'; s1++); // find the end of s1
for (; *s2!='\0'; *s1=*s2, s1++, s2++); // concatenate s2 to s1
*s1 = '\0';
}
securitylevels f2() {
return (non_secret-black_ops)/3<secret?black_ops:non_secret;
}
int getValue(int A[][2], int B[][2], int n, int row, int column) {
int value = 0;
for (int k=0; k<n; k++) {
value = value + A[row][k]*B[k][column];
}
return value;
}
void f3(int A[2][2], int B[2][2], int C[2][2], int n) {
for (int row=0; row<n; row++) {
for (int column=0; column<n; column++) {
C[row][column] = getValue(A, B, n, row, column);
}
}
}
int f4(int i) { // return i+1
int *p = &i, *q = p; // p, q are both pointers to i
return ++(*q);
}
double f5(int a, int b) {
double t = ((a--)/3+b%4); // a-- means a is used first, then decremented
return t;
}
void f6(int D[], int n) {
int i=0, m=0;
m = n>>4; // clear the last 4 bits of n, and store the result in m
m = m<<4;
for (int i=7; i>=4; i--) { // if the i-th bit of n is 1, then the i-th element of D is 1
D[i] = (m&(1<<i))?1:0;
}
}
int main() {
char s1[100] = "NTUT", s2[100] = "Good";
int A[2][2] = {{1,2}, {2,1}}, B[2][2] = {{1,2}, {2,3}}, C[2][2], D[10];
f1(s1, s2);
printf("%s\n", s1); /* Problem 1-1 */
printf("%d\n", f2()); /* Problem 1-2 */
f3(A,B,C,2);
printf("%d %d\n", C[0][0], C[1][1]); /* Problem 1-3 */
printf("%d\n", f4(20)); /* Problem 1-4 */
printf("%3.2f\n", f5(6, 6)); /* Problem 1-5 */
memset(D, 0, sizeof(D)); f6(D, 123); // 假設array D被初始化為0
printf("%d%d%d\n", D[1], D[3], D[5]); /* Problem 1-6 */
}
```
### 說明
#### $f1()$
- ++line 05++ 為將s1的指標移到s1的結尾(即`'\0'`的位置)。
- ++line 06++ 為將s2的字元一個一個複製到s1 (`*s1 = *s2`),直到s2的結尾(即`'\0'`的位置)。
- 故 $f1()$ 為 ==字串串接(concatenation)== 的函數,將s2的字串串接到s1的後面。
#### $f2()$
- $securitylevels$ 是一個==列舉型別(enum)==,它包含四個值:$black_ops$、$top_secret$、$secret$ 和 $non_secret$。這四個值在內部都會被賦予一個整數值,預設從 $0$ 開始,並按照它們在列舉中的順序遞增。也就是說,$black_ops$ 的值為 $0$,$top_secret$ 的值為 $1$,$secret$ 的值為 $2$,$non_secret$ 的值為 $3$。
- `(condition)?x:y` 是==三元運算子(Ternary Operator)==,如果condition為真,則回傳x,否則回傳y。
- `return (3-0)/3<2?0:3`,condition部分為 $1<2$,故回傳 $0$。
#### $f3()$
- $f3()$ 為==矩陣相乘(matrix multiplication)==的函數,將 $A$ 和 $B$ 兩個 $2\times 2$ 的矩陣相乘,結果存入 $C$。
$$
A = \begin{bmatrix}
1 & 2 \\
2 & 1
\end{bmatrix},
B = \begin{bmatrix}
1 & 2 \\
2 & 3
\end{bmatrix},
C = \begin{bmatrix}
5 & 8 \\
4 & 7
\end{bmatrix}
$$
#### $f4()$
- $p$ 和 $q$ 都是指向 $i$ 的指標,所以 $*p$ 和 $*q$ 都是 $i$,因此 $f4(i)$ 為將 $i$ 加一後回傳的函數。
#### $f5()$
- 若 ==decrement operator== `--`
- 在變數後面,則先使用變數,再遞減(先用再減);
- 在變數前面,則先遞減,再使用變數(先減再用)。
- 故 $f5(6, 6)$ 為 $((6--)/3+6\%4) = 6/3+2 = 2+2 = 4$,再轉型為 `double` 型態,返回 $4.0$。
- `printf("%3.2f\n", f5(6, 6));` 表示要輸出一個浮點數,整數部分至少有 3 位,小數部分有 2 位,故輸出 $4.00$。
:::success
📌 其他考點:
- 需注意 `(int) / (int)` 為整數除法,結果為整數,若要轉型為 `double`,則至少有一個運算數為 `double`。
- 若將題目改成 `double t = ((a--)/4+b%4);`,則結果為 $1+2 = 3.0$。
- 若將題目改成 `double t = ((a--)/4.0+b%4);`,則結果為 $1.5+2 = 3.5$。
:::
#### $f6()$
- $m = n>>4$ 和 $m = m<<4$ 的作用是清除 $n$ 的最後 4 位,即將 $n$ 的最後 4 位設為 0,並將結果存入 $m$。
- `D[i] = (m&(1<<i))?1:0;` 的作用是判斷 $n$ 的第 $i$ 位是否為 1,若為 1,則 $D[i]$ 設為 1,否則設為 0。
- $n = 123$ 的二進位為 $1111011$,故 $m = 112$,其二進位為 $1110000$,故 $D[4] = 1, D[5] = 1, D[6] = 1, D[7] = 0$。
- 若array $D$ 未初始化,則未初始化的變數具有不確定的值,可能會導致 ==Undefined behavior (UB)==。
- 假設array $D$ 被初始化為 $0$,則 $D[1] = 0, D[3] = 0, D[5] = 1$,故輸出為 $001$。
## Problem 3 (1)
### 題目
Please trace the following `Python` program and answer the output of each print() statement for problems (3-1~3-7).
### 答案
1. `6`
2. `2`
3. `13`
4. `[['Bob', 50, 70]]`
5. `utod`
6. `[8, 16, 6]`
7. `bc`
### Code
```python=
def f1(n):
X = [i for i in range(1, n) if i % 2 == 0]
return (X)
def f2(S):
data = dict()
for s in S:
if s in data.keys():
data[s] = data[s]+1
else:
data[s] = 1
return data
def f3(n):
f = lambda m,n: m*n+1
X = {i: f(i, i+1) for i in range(1, n) if i % 3 == 0}
return (X)
def f4():
scores = [['John', 90, 80], ['Bob', 50, 70], ['Mary', 80, 70]]
data = filter(lambda x: True if sum(x[1:3]) < 130 else False, scores)
return list(data)
def f5(s1, s2, m, n):
return s1[m:n]+s2[m:n]
def f6(A, B):
return list(map(lambda x, y: x**y, A, B))
def f7(data,n):
if n==len(data): return [data]
elif n==0: return ['']
s0 = f7(data[1:], n)
s1 = [data[0] + s for s in f7(data[1:], n-1)]
return sorted(s0+s1)
print(f1(8)[2]) # (Problem 3-1)
print(f2('ntut csie')['t']) # (Problem 3-2)
print(f3(6)[3]) # (Problem 3-3)
print(f4()) # (Problem 3-4)
print(f5('ntut','good',2,4)) # (Problem 3-5)
print(f6([2,4,6], [3,2,1])) # (Problem 3-6)
print(f7('abc', 2)[2]) # (Problem 3-7)
```
### 說明
#### $f1()$
- ==List Comprehension== 是一種用來創建列表的方法,它提供了一個簡潔的方法來創建列表。
- `X = [i for i in range(1, n) if i % 2 == 0]` 為創建一個列表,列表中的元素為 $1$ 到 $n-1$ 中的偶數。
- 故 $f1(8)[2]$ 為 $[2, 4, 6]$ 中的第 $3$ 個元素,即 $6$。
- 展開後的程式碼為:
```python
def f1(n):
# X = [i for i in range(1,n) if i%2==0]
X = []
for i in range(1,n): # in C/C++: for(int i=1; i<n; i++)
if i % 2 == 0:
X.append(i)
return (X)
```
#### $f2()$
- 使用 `dict()` 創建一個空字典,若 `s` 在字典中,則將 `s` 的值加一,否則將 `s` 加入字典中,並設為 $1$。
- 即為計算字串 `S` 中每個字元的出現次數。
- 故 `f2('ntut csie')['t']` 為字串 `'ntut csie'` 中字元 `t` 的出現次數,即 $2$。
:::success
📌 `python` 中的 `Counter` 可以簡單的達成同樣的效果。
```python
from collections import Counter
cnt = Counter(S)
```
:::
#### $f3()$
- 定義一個名為 $f$ 的 ==Lambda 函數==。Lambda 函數是一種匿名函數,在 `Python` 中,我們可以使用 `lambda` 關鍵字來創建 `lambda` 函數。
- 使用 ==Dictionary Comprehension== 創建一個字典,字典中的鍵為 $1$ 到 $n-1$ 中的 $3$ 的倍數,值為 $f(i, i+1)$。
- 故 `f3(6)[3]` 為字典 `{3: f(3, 4)}` 中的第 $1$ 個元素,即 $13$。
#### $f4()$
- `filter( function, iterable )`,`function` 為過濾函數,`iterable` 為可迭代對象。`filter` 函數的作用是對序列中的元素進行過濾,返回一個迭代器,迭代器中的元素是滿足條件的元素。
- 使用 ==filter 函數== 對 `scores` 中的每個元素進行過濾,條件為 `sum(x[1:3]) < 130`。
- 故 `f4()` 為過濾後的結果,即 `[['Bob', 50, 70]]`。
- 展開後的程式碼為:
```python
def isLessThan130(x):
if sum(x[1:3]) < 130:
return True
else:
return False
def f4():
scores=[['John',90,80],['Bob',50,70],['Mary',80,70]]
# filter( function, iterable )
data = filter(isLessThan130, scores)
return list(data)
```
#### $f5()$
- $s1[m:n]$ 為取字串 $s1$ 中從第 $m$ 個字元到第 $n-1$ 個字元的子字串,若 $n$ 超過字串長度,則取到字串結尾。
- 故 `f5('ntut','good',2,4)` 為 $'ntut'[2:4]$ 和 $'good'[2:4]$ 的串接,即 $'ut'+'od'$,即 $'utod'$。
#### $f6()$
- `map( function, iterable )`,`function` 為映射函數,`iterable` 為可迭代對象。`map` 函數的作用是對序列中的每個元素進行映射,返回一個迭代器,迭代器中的元素是映射後的結果。
- 使用 ==map 函數== 對 `A` 和 `B` 中的每個元素進行映射,映射函數為 `lambda x, y: x**y`。
- 故 `f6([2,4,6], [3,2,1])` 為 $[2^3, 4^2, 6^1]$,即 $[8, 16, 6]$。
- 展開後的程式碼為:
```python
def f6(A,B):
f = lambda x, y :x**y # exponentiation x^y, f(x,y) = x**y
res = []
for x, y in zip(A,B):
res.append(f(x,y))
return res
```
:::success
📌 對於一些需要手動處理輸出入的 Online Judge(OJ) 平台,很常會用到map函數,例如:
```python
arr = list(map(int, input().split()))
```
:::
#### $f7()$
- $f7()$ 為一個 ==遞迴(Recursion)函數==,遞迴函數是一個函數在其函數體中調用自己的函數。
- $f7(abc, 2) = ['ab', 'ac', 'bc']$
- $f7(bc, 2) = ['bc']$
- $f7(bc, 1) = ['b', 'c']$
- $f7(c, 1) = ['c']$
- $f7(c, 0) = ['']$
- $s0 = ['c']$
- $s1 = ["b" + s for s in ['']] = ["b"]$
- $s0 = ['bc']$
- $s1 = ["a" + s for s in ["b", "c"]] = ["ab", "ac"]$
- 故 `f7('abc', 2)[2]` 為 $['ab', 'ac', 'bc']$ 中的第 $3$ 個元素,即 $'bc'$。
:::success
📌 在 `python` 中,單引號 `''` 和 雙引號 `""` 是等價的,都可以用來表示字串。
:::
## Problem 3 (2)
### 題目
Suppose that the outputs of the following `Python` program are as follows:
- $[[5, 7], [19, 21]]$
- $[[5, 7, 9], [19, 21, 23], [33, 35, 37]]$
- $[[9, 12], [30, 33]]$
Please trace the program and fill the blanks with correct statements.
### 答案
8. `data[i][j]`
9. `(n*n)` 或 `n**2` ,前者因為 `*` 的operator precedence較低,所以要用括號括起來。
10. `m//n`
### Code
```python=
def op(data, x, y, n):
sum = 0
for i in range(x, x+n):
for j in range(y, y+n):
sum = sum + data[i][j] # (Problem 3-8)
return sum//(n*n) # (Problem 3-9)
def compress(data, m, n):
size = m//n # (Problem 3-10)
target = []
for x in range(size):
row = []
for y in range(size):
row.append(op(data, x*n, y*n, n))
target.append(row)
return target
data = [[1, 2, 3, 4, 5, 6, 7],
[8, 9, 10, 11, 12, 13, 14],
[15, 16, 17, 18, 19, 20, 21],
[22, 23, 24, 25, 26, 27, 28],
[29, 30, 31, 32, 33, 34, 35],
[36, 37, 38, 39, 40, 41, 42],
[43, 44, 45, 46, 47, 48, 49]]
print(compress(data, 4, 2), '\n##')
print(compress(data, 6, 2), '\n##')
print(compress(data, 6, 3), '\n##')
#(1+2+8+9)//4=5
#(3+4+10+11)//4=7;
#(15+16+22+23)//4=19,
#(17+18+24+25)//4=21
...
#(1+2+3+8+9+10+15+16+17)//9=9,
#(4+5+6+11+12+13+18+19+20)//9=12,
#(22+23+24+29+30+31+36+37+38)//9=30,
#(25+26+27+32+33+34+39+40+41)//9=33
```
### 說明
1. 由function名稱`compress`可知是壓縮的意思,所以是將原本的矩陣壓縮成較小的矩陣。
2. 壓縮可以將一定範圍內的數值取平均/取中位數/取最大值/取最小值等等,從++line 28-36++ 的註解可以看出是取平均。
3. 故 `op()` 函數的作用是取一定範圍內的數值的平均值,並返回結果。也就是以 $(x, y)$ 為左上角,長寬皆為 $n$ 的矩陣的平均值。
4. 從 ++line 25-26++ 的輸出結果可以看出,前 $2 \times 2$ 的內容一樣,故可以推測出 $m$ 為壓縮前的矩陣的長度,因此壓縮後矩陣的長度 $size = m//n$。
## Problem 3 (3)
### 題目
Suppose that the outputs of the following Python program are as follows:
$$
\begin{aligned}
& (0,0)(0,1)(0,2)(0,3) \\
& (1,0)(1,1)(1,2)(1,3) \\
& (2,0)(2,1)(2,2)(2,3) \\
& (3,0)(3,1)(3,2)(3,3)
\end{aligned}
$$
Please trace the program and fill the blanks with correct statements.
### 答案
11. `//`
12. `%`
### Code
```python=
def printSquare(n):
i = 0
while i < n*n:
print((i//n, i % n), end='') # (Problem 3-11), (Problem 3-12)
i = i+1
if i % n == 0:
print()
printSquare(4)
```
### 說明
- 觀察輸出結果可以得知,這段程式碼的目的是輸出一個 $n \times n$ 的方陣,方陣中的元素為 $(i, j)$,其中 $i$ 為行,$j$ 為列。
- 從 $0$ 開始,每次輸出 $(i//n, i\%n)$,其中
- `//` 是整除運算符,返回商的整數部分。
- `%` 是取餘運算符,返回除法的餘數。
## Problem 4 ~[TODO]~
### 題目
### 答案
### Code
### 說明
## Problem 5 ~[TODO]~
### 題目
### 答案
### Code
### 說明
<style> :root{
--markup-code: #000033;
}
.markdown-body code,
.markdown-body tt {
background-color: #ffcccc;
color: var(--strong-code);
}
.markdown-body strong{
color: var(--strong-code);
font-weight:700
}
.markdown-body code{
color: var(--markup-code)!important
}
</style>