# 2019q1 Homework3 (review)
contributed by < `LiunuXXX` >
## 預期目標
* 回顧 Linux 課程進行狀況
* 隨堂測驗回顧
* 誠實面對自己
## 作業要求
* 從 [第 1 週](https://hackmd.io/s/SyrZMGYr4), [第 2 週](https://hackmd.io/s/H1Pik8M8E), [第 3 週(上)](https://hackmd.io/s/S1weT4iLE), [第 3 週(下)](https://hackmd.io/s/SkrVSKiU4), [第 4 週(上)](https://hackmd.io/s/H1KLoTEv4), [第 4 週(下)](https://hackmd.io/s/BJKk1ANv4) 選出你感興趣的 3 個題組進行作答,並且回答附帶的「延伸問題」
* 比照 [課前測驗參考解答: Q1](https://hackmd.io/s/ByzoiggIb) 和 [對 Linked List 進行氣泡排序](https://hackmd.io/s/BklGm1MhZ) 的模式來撰寫共筆,需要詳細分析自己的想法、參閱的材料 (包含 C 語言規格書的章節),以及==進行相關實驗==
* 若有不能理解的部分,請標註出來。善用 HackMD 的語法 `:::info` 和 `:::` 標註你的提問
:::info
像是這樣標註提問
:::
## 作業區
---
### 2019q1 第一週測驗題
### 測驗 `1`
西洋棋中的皇后可以直線前進,吃掉遇到的所有棋子,如果棋盤上有 8 個皇后,則這 8 個皇后如何相安無事地放置在棋盤上,1970 年與 1971 年, E.W.Dijkstra 與 N.Wirth 曾用這個問題來講解程式設計之技巧。
關於棋盤的問題,都可以用遞迴求解,然而如何減少遞迴的次數?在八個皇后的問題中,不必要所有的格子都檢查過,例如若某列檢查過,該該列的其它格子就 不用再檢查了,這個方法稱為分支修剪。
![](https://i.imgur.com/Ntc7TDQ.png)
所以檢查時,先判斷是否在已放置皇后的可行進方向上,如果沒有再行放置下一個皇后,如此就可大大減少遞迴的次數,例如以下為修剪過後的遞迴檢 查行進路徑:
![](https://i.imgur.com/0aUWpaI.png)
8 個皇后的話,會有 92 個解答,如果考慮棋盤的旋轉,則旋轉後扣去對稱的,會有 12 組基本解。 [ [source](https://openhome.cc/Gossip/AlgorithmGossip/EightQueen.htm) ]
透過 [bitwise 演算法](http://jgpettibone.github.io/bitwise-n-queens/) 改寫為以下 C 程式:
```cpp
#include <stdio.h>
#include <stdlib.h>
static int sol_count = 0;
void recursive_solver(int row, int maj_con, int min_con, int col_con, int n)
{
int queen_position;
int conflicts = min_con | maj_con | col_con;
int i = 0;
min_con &= 65535;
while (i < n) {
queen_position = 1 << (A1 - i);
if (!(queen_position & conflicts)) {
if (row == n - 1)
sol_count++;
else
recursive_solver(row + A2, (maj_con | queen_position) >> A3,
(min_con | queen_position) << A4,
col_con | queen_position, n);
}
i++;
}
}
void solve_queens(int n)
{
int minor_dconflict = 0, major_dconflict = 0, column_conflict = 0;
recursive_solver(0, column_conflict, major_dconflict, minor_dconflict, n);
printf("solutions = %d\n", sol_count);
}
```
其中 `solve_queens` 的參數 `n`,數值介於 `4` 到 `16`。
請補完程式碼。
==作答區==
A1 = ?
* `(a)` 16
* `(b)` 15
* `(c)` 14
* `(d)` 13
* `(e)` 12
* `(f)` 11
* `(g)` 10
* `(h)` 9
* `(i)` 8
A2 = ?
* `(a)` 7
* `(b)` 6
* `(c)` 5
* `(d)` 4
* `(e)` 3
* `(f)` 2
* `(g)` 1
* `(h)` 0
A3 = ?
* `(a)` 7
* `(b)` 6
* `(c)` 5
* `(d)` 4
* `(e)` 3
* `(f)` 2
* `(g)` 1
* `(h)` 0
A4 = ?
* `(a)` 7
* `(b)` 6
* `(c)` 5
* `(d)` 4
* `(e)` 3
* `(f)` 2
* `(g)` 1
* `(h)` 0
### Solution
#### A1
* 利用 conflicts 記錄會和皇后產生衝突的位置
```cpp=8
int conflicts = min_con | maj_con | col_con;
```
* 隨著遞迴不斷增長,min_con 可能會超出棋盤邊界,而 solve_queens 的參數介於 4~16,棋盤應為 16x16,因此用 `0xffff`(65536) 去 mask。
接著看 while 迴圈:
```cpp=12
while (i < n) {
queen_position = 1 << (A1 - i);
if (!(queen_position & conflicts)) {
if (row == n - 1)
sol_count++;
else
recursive_solver(row + A2, (maj_con | queen_position) >> A3,
(min_con | queen_position) << A4,
col_con | queen_position, n);
}
i++;
}
```
* 皇后的位置在 15~0 這 16 個位置,而 `queen_position` 又隨著 while 迴圈減少,因此最大位置應該是在 15,也就是 `1000 0000 0000 0000`
* 故得 A1 = 15
#### A2
```cpp=28
recursive_solver(0, column_conflict, major_dconflict, minor_dconflict, n);
```
```cpp=18
recursive_solver(row + A2, (maj_con | queen_position) >> A3,
(min_con | queen_position) << A4,
col_con | queen_position, n);
```
由題目的遞迴參數可知,row 數由 0 逐漸增加,可知為從第
一 row 往下逐列呼叫。
* 故 A2 = 1
#### A3,A4
```cpp=18
recursive_solver(row + A2, (maj_con | queen_position) >> A3,
(min_con | queen_position) << A4,
col_con | que
```
* 假設 queen 在第一列的 15 位置 (`1000 0000 0000 0000`),則遞迴呼叫下一 row 時,conflict 位置為 14 的位置 (`0100 0000 0000 0000`),即右移一個 bit。
* 故 A3 = 1
* 類似 major conflict,但 min conflict 是左移
* 故 A4 = 1