## 資訊之芽 week12 ## 北區作業題解 --- ## 20 - 中國人排隊問題 ---- * 排隊的問題上課有講過可以用:`queue` * 但是他多了一個神奇的條件: * 如果隊伍中已經有了自己的「同夥的」 * 就直接插隊排在自己「同夥的」的後面 * 要怎麼用 `queue` 呢? ---- ### 觀察 * 在插隊的時候,是插在「同夥的」的後面 * 拿公仔也會是「同夥的」第一個進去的人先拿 * 不覺得這樣的運作方式也很像是某資料結構嗎? ---- ### 觀察 * 「同夥的」內部運作符合「先進先出」的順序 * 所以可以用 `queue` 來維護「同夥的」的資訊! ---- ### 觀察 * 「同夥的」內部運作符合「先進先出」的順序 * 所以可以用 `queue` 來維護「同夥的」的資訊! * 於是我們可以得到一個 `queue` 中 `queue` 的作法 ---- ### 實作 * 因為「同夥的」一定都會排在一起 ---- ### 實作 * 因為「同夥的」一定都會排在一起 * 所以可以開一個紀錄「同夥編號的」`queue`,紀錄現在排在隊伍的 **「同夥的」的團體編號** ---- ### 實作 * 因為「同夥的」一定都會排在一起 * 所以可以開一個紀錄「同夥編號的」`queue`,紀錄現在排在隊伍的 **「同夥的」的團體編號** * 用很多 `queue` 來維護每組「同夥的」內部的資訊 ---- * 因為「同夥的」一定都會排在一起 * 所以可以開一個紀錄「同夥編號的」`queue`,紀錄現在排在隊伍的 **「同夥的」的團體編號** * 用很多 `queue` 來維護每組「同夥的」內部的資訊 * 這樣會有一個記編號的 `queue` * 跟 $N$ 個維護「同夥的」內部資訊的 `queue` ---- ### 實作細節一 * 存「團體編號的」queue:`mainQueue` * 用很多 `queue` 來維護每組「同夥的」內部的資訊 * \> 可以用 `queue` 的陣列! ```cpp= queue<int> mainQueue; queue<int> que[1005]; ``` ---- ### 實作細節二 * 對於每個人,需要知道他是哪一團的 * \> 開一個很大的陣列 `teams` (大小是人們的編號) * 用 `teams[index]` 來存`index` 這個人是哪一組 ---- ### 實作細節二 * 對於每個人,需要知道他是哪一團的 * \> 開一個很大的陣列 `teams` (大小是人們的編號) * 用 `teams[index]` 來存`index` 這個人是哪一組 * (update) <b style="color:red">有可能有人不在任何一個團體裡面</b> * (update) `teams[index]` 可以存 `-1` 來表示 ---- ### 實作細節三 * 對每個團體,需要知道有沒有「同夥的」在排隊 * \> 開一個陣列 `inQueue` (大小是團體的數量) * 用 `inQueue[team]` 來存同夥編號 `team` 的有沒有人在排隊 ```cpp= bool inQueue[N]; // N 是團體的數量 ``` ---- ### 實作細節四 * (update) 因為有可能有人不在任何團體 * 所以 `mainQueue` 裡面不能只放團體編號 * 可以把 `mainQueue` 內容改成放團體的某個「人」 * 就可以用 `teams` 的陣列取得那個人所屬的團體 * **這樣沒有同夥的人就可以是自己一組** ---- ### 實作順序 1. 讀輸入,順便紀錄 `teams` 陣列 * 也就是每個人是哪一組 2. 處理 ENQUEUE x / DEQUEUE 操作 ---- ### ENQUEUE x 操作 1. 先取得 `x` 在哪一組 (`team = teams[x]`) 2. (update) 先檢查 `team` 是不是 `-1`,如果是的話就直接加到 `mainQueue` (因為他自己一組) 3. 檢查有沒有同夥的在排隊:用 `inQueue[team]` 4. 如果有的話直接插進去 `que[team]` 裡面 5. 沒有的話在 `mainQueue` 新增 `team`,把 `inQueue[team]` 設成 `true`,再讓那個人加到 `que[team]` ---- ### DEQUEUE 操作 1. 用 `mainQueue.front()` 取排最前面的團體編號 2. (update) 先檢查 `team` 是不是 `-1`,如果是的話就直接從 `mainQueue` 移掉就直接結束 3. 把 `que[team]` 的第一個人移除掉 3. 檢查 `que[team]` 還有沒有人在排隊,如果沒有就把 `inQueue[team]` 設成 `false`,並且把 `mainQueue` 第一個團體刪掉 ---- ### pseudo code ```python= int teams[1000000]; queue<int> mainQueue, que[1000]; bool inQueue[1000]; # 輸入 for _ from 1 to T: # 把 mainQueue, que[1000], inQueue, teams 全部設為 0 (初始化) for team_idx from 0 to N-1: person_idx = read() teams[person_idx] = team_idx for i from 0 to M-1: if ENQUEUE: x = read() team = teams[x] if team == -1: # x 自己一組 # 直接在 mainQueue 裡面新增 x # 要記得 continue,不然就會繼續跑下去了 if que[team].empty(): # 沒有「同夥的」在排隊 # mainQueue 新增 x # 把 inQueue[team] 設成 `true` # 再把那個人 (x) 加到 que[team] else: # 把那個人 (x) 加到 que[team] if DEQUEUE: # frontTeam = mainQueue 的第一個隊伍 if frontTeam == -1: # 第一個人自己一組 # 直接輸出 + 從 mainQueue 移除掉即可 # 要記得 continue,不然就會繼續跑下去了 # 移除掉 que[frontTeam] 的第一個人,順便輸出他 if que[frontTeam].empty(): # 沒有人了! # 把 mainQueue 第一個團體刪掉 # 把 inQueue[frontTeam] 設成 false ``` ---- ### 參考程式碼 (尚未公布) --- ## 1111 - 撲克牌排序問題 ---- * 這題在考「自定義 `sort` 函式的排序方式」 * 可以使用 `sort(a, a + n, compare)`,然後定義 `compare` 這個 function * 還有簡單的字串處理 ---- ### 自定義排序 1. 如果有人是 Joker 先 return 2. 分別取得兩個字串的數字 (字串處理) 3. If 數字不一樣,直接 return 小排到大 4. If 數字一樣,取得花色,return 小排到大 * 有發現花色有按照字典序嗎? ---- ### 字串處理 * 取得花色可以看第一個位置就好 ---- ### 字串處理 * 取得數字可以用字串的「長度」作輔助 * Joker 長度一定是 $1$ * 數字「$10$」長度一定是 $3$ * 其他長度都是 $2$ * 特判 $A,J,Q,K$ 這四個符號 * 剩下的就可以直接用數字得到結果了 ---- ### pseudo code ```cpp= string cards[1000]; int get_value(string s) { // TODO: 實作拿到數字的函式 int size = s.size(); // ... } bool compare(string a, string b) { if a 或 b 是 joker: // return something // 拿到數字 int val_a = get_value(a), val_b = get_value(b); if val_a != val_b: // 不同數字 // return something // 同數字,不同花色 // return something } int main() { // 輸入 n // 輸入 cards sort(cards, cards + n, compare); // 輸出 cards } ``` ---- ![image](https://hackmd.io/_uploads/BJQ7-JJ4A.png)
{"slideOptions":"{\"theme\":\"solarized\",\"transition\":\"slide\"}","description":"這題看起來很queue,但是他多了一個神奇的條件如果隊列中已經有了自己小組的成員,他就直接插隊排在自己小組成員的後面。","contributors":"[{\"id\":\"fabda494-b016-4916-8ef2-8c79b25f079b\",\"add\":5237,\"del\":662}]","title":"資訊之芽 week12 北區作業題解"}
    210 views