# 資訊之芽 Week12 竹區作業題解 [TOC] ## NEOJ 1234 https://neoj.sprout.tw/problem/1234/ ### 解題思路 & Code - $head$ 不指向 $nullptr$ ![image](https://hackmd.io/_uploads/r17rjh0QA.png) - $head$ 指向 $nullptr$ ![image](https://hackmd.io/_uploads/H1QLs3CXC.png) #### void append(Node *&head, int value) 在 linked list 尾端新增值為 value 的節點。 1. 先判斷 $head$ 是否為 $nullptr$ - 若是,則將新節點直接設為 $head$ - 若否,進 step 2 2. 找到 linked list 的最後一個節點,讓最後一個節點指向新節點 ```cpp void append(Node *&head, int value) { Node *new_node = new Node; new_node->value = value; new_node->next = nullptr; // TODO: 判斷 head 是否為 nullptr if (head == nullptr) { // .... return; } Node *cur = new Node; cur = head; while (cur->next != nullptr) { // 讓 cur 指向下一個節點 // ... } // 最後一個節點指向新結點 // .... } ``` #### void insert(Node *&head, int index, int value) 將值為 value 的節點插入 linked list 索引值為 index 的位置。 1. 判斷 index 是否為 0,index 為 0 代表新結點會變成新的 $head$ - 若是,讓新節點成為 $head$ - 若否,進 step 2 2. 找出 linked list 的第 index-1 個節點 $cur$ 1. 新節點指向 $cur$ 的 $next$ 2. $cur$ 的 $next$ 指向新節點 - 隨時注意是否指向 $nullptr$ ! ```cpp void insert(Node *&head, int index, int value) { Node *new_node = new Node; new_node->value = value; new_node->next = nullptr; // TODO: 如果 index 為 0 if (index == 0) { // ... return; } // TODO: 讓 cur 指向第 index-1 個節點 Node *cur = head; for (int i = 0; i < index - 1; i++) { // warning if (...) { cur = cur->next; } } // 本題不需要下面這個判斷,但正常來說是必要的 ! if (cur == nullptr) { delete new_node; return; } // TODO: 接上新的節點 } ``` #### void deleteNode(Node *&head, int value) 在 linked list 刪除值為 value 的節點 1. 判斷 $head$ 是否為 $nullptr$ - 若是,什麼都不做 - 若否,進 step 2 2. 檢查 $head$ 是否為被刪除的節點 - 若是,新增一個指標 $M$ 指向 $head$,讓 $head$ 移到下一個節點,刪掉 $M$ 指向的節點 - 若否,進 step 3 3. 找出 "要被刪除的節點" $del$ 的 "前一個節點" $pre$ - $pre$ 的下一個節點即為 $del$,需要被刪除 - 因此先讓一個指標 $M$ 指向 $del$,讓 $pre$指向下兩個節點,再刪掉 $M$。 ```cpp void deleteNode(Node *&head, int value) { // 判斷 head 是否為 nullptr if(head==nullptr){ return; } // 檢查 head 是否為被刪除的節點 if(head->value == value){ // ... return; } // 找出 "要被刪除的節點" del 的 "前一個節點" pre Node *pre = head; while (pre->next != nullptr && pre->next->value != value) { pre = pre->next; } // 刪除節點 if (pre->next != nullptr) { // .... } } ``` #### void print(Node *head) 印出 linked list 所有值,每印出一個值請換行,包含最後一個值也要換行! 1. 讓指標 $cur$ 指向 $head$ 2. 只要 $cur$ 不為 $nullptr$,就印出值並指向下一個節點 ```cpp void print(Node *head) { Node *current = head; while (current != nullptr) { // TODO: 印出值並指向下一個節點 } } ``` ## NEOJ 1111 https://neoj.sprout.tw/problem/1111/ ### 解題思路 ![image](https://hackmd.io/_uploads/rJWdF1kV0.png) - 用==C-Style 字串==存取每張牌的資訊,並用 vector 把這些資訊存在一起。 (若會使用 string , 則不須使用 C-Style 字串) - 用 ==std::sort()== 對全部的牌==由小到大==排序。 - 在自定義的 compare function 裡,依照題目給的花色大小、數字大小做比較。 比較的優先級如下敘述 $:$ 1. $Joker$ 最大,最先判斷是否有 $Joker$,$Joker$ 的資訊是字串長度為 $1$ 2. 分別抓出字串的花色 $suit$ 與數字 $number$。==注意 $number$ 的長度== 3. 比較 $number$ 大小。 4. 比較 $suit$ 大小 ### Pseudo Code ```cpp #include <algorithm> #include <iostream> #include <string> #include <vector> using namespace std; int get_suit_rank(char c) { // TODO: 自己定義各個花色的大小,並回傳 } int get_card_value(string c) { // TODO: 將卡牌的 number 由 string 回傳成 int } bool compare(string a, string b) { // TODO: 判斷是否為 Joker // Joker 最大,最先判斷是否有 Joker,若有,則直接回傳。Joker 的資訊是字串長度為 1 // .... // 抓出字串的花色 suit char a_suit = a[0]; char b_suit = b[0]; // TODO: 抓出字串的數字 number string a_number = ... string b_number = ... // TODO: 比較數字大小 if (get_card_value(a_number) != get_card_value(b_number)) return ... // TODO: 比較花色 if (get_suit_rank(a_suit) != get_suit_rank(b_suit)) return ... return false; } int main() { // 輸入卡牌 int number_of_cards; cin >> number_of_cards; vector<string> card(number_of_cards); for (int i = 0; i < number_of_cards; i++) { cin >> card[i]; } // 排序 sort(card.begin(), card.end(), compare); // TODO : 輸出 // .... } ``` ## NEOJ 20 https://neoj.sprout.tw/problem/20/ ### 使用到的變數 - 團體數量 - 假設為 $N$ - 紀錄人與團體之間的關係 `person_to_group` - 用一個 vector `person_to_group` 來記錄每個人所在的群組 - `person_to_group` 大小設置為 1000000(題目有提到人的編號為 0 ~ 999999) - 初始值設置為 -1 表示該人不屬於任何群組。 - 輸入每個成員會屬於哪一個群組,這時就更新 `person_to_group`,使其記錄每個人對應的群組編號。 - 主隊列 `main_queue` - 管理各個團體排隊的順序,存放團體的編號 如下所示: 主隊列中,團體排隊的順序分別為 5, 7, ..., 1, 51, 93。 ![image](https://hackmd.io/_uploads/HypYtyk4A.png) - 子隊列 `group_queues` - 大小為 $N$ - 每個團體都有一個隊列來管理其成員的排隊順序。 如下所示: - 子隊列 5 `group_queues[5]` 裡面有編號 12。 - 代表主隊列之中,所屬團體 5 的人只有編號 12。 - 且在主隊列中的排隊順序為 12。 - 子隊列 7 `group_queues[7]` 裡面有編號8, 10, ..., 4, 13 - 代表主隊列之中,所屬團體 7 的人只有編號 8, 10, ..., 4, 13。 - 且在主隊列中的排隊順序為 8, 10, ..., 4, 13。 ![image](https://hackmd.io/_uploads/SypcYyJVC.png) - 記錄每個團體是否在主隊列中 `in_main_queue` - 大小為 $N$ - 初始值為 false。代表一開始所有團體都不在隊列之中 ### 解題思路 - `ENQUEUE X` - 用 `person_to_group` 取得編號為 x 的人所屬的團體 - 檢查是否為單人,不屬於任何群組 (若為 -1 表示該人不屬於任何群組。) - 若是,幫他創一個單人團體 `new_queue`,把`new_queue`加進 `group_queues`,並更新他的`person_to_group`、`in_main_queue` - 若否,進入下一步 - 檢查該人所屬團體是否在主隊列之中 - 若否,將該團體加進 `main_queue`,並更新相關變數 - 若是,進入下一步 - 將該人加入其團體的子隊列 - `DEQUEUE X` - 用`main_queue`取得最前面的團體 - 取出該團體子隊列中的最前面的一個人 - 檢查該團體的`group_queue`是否為空 - 若是,代表沒有人在排隊了,將其從主隊列中移除,並跟新`in_main_queue` - 若否,進入下一步。 - 輸出取出來的人的編號。 ### Pseudo Code ```cpp #include <iostream> #include <queue> #include <vector> using namespace std; int main() { int t; cin >> t; for (int test_case = 1; test_case <= t; test_case++) { cout << "Line #" << test_case << endl; int n; cin >> n; vector<int> person_to_group(1000000, -1); for (int i = 0; i < n; i++) { // TODO: 更新人的所屬團體 // ... } queue<int> main_queue; vector<queue<int>> group_queues(n); vector<bool> in_main_queue(n, false); int m; cin >> m; for (int i = 0; i < m; i++) { string command; cin >> command; if (command == "ENQUEUE") { int x; cin >> x; int group = person_to_group[x]; // 如果這個人單獨一組,do something...... if (group == -1) { // ... } // 如果不在主隊列之中 if (!in_main_queue[group]) { // ... } group_queues[group].push(x); } else if (command == "DEQUEUE") { // 用`main_queue`取得最前面的團體 // ... // 取出該團體子隊列中的最前面的一個人 // ... // TODO: 如果子隊列內已經沒人 if (group_queues[front_group].empty()) { // ... } cout << person << endl; } } } return 0; } ```