基礎資料結構3.2——佇列
題目上添加了超連結,大家點一下題目就會自動跳轉到Poj原題介面~~ 衝鴨衝鴨ヾ(◍°∇°◍)ノ゙。
佇列相較於其它資料結構的特點是先進先出,通常也是通過陣列、連結串列實現。
通過陣列實現時,為解決偽溢位問題,常需要採用迴圈佇列。做題時用陣列實現就足以應對絕大部分題目。
常見題型有模擬、單調佇列(挺難的,也是很多進階優化演算法的基礎)、以及後續的搜尋題....很重要,需要打好基礎。
最後一道題2823尤為經典重要。相關課程我看的少,有感覺講的特別好、特別淺顯深刻的,歡迎大家留言分享(好想自己錄一個)
3.2.1 Card Trick (3032)
題意:一堆牌有n張,從牌堆頂部開始,第i次將i張牌放到牌底,然後將新的牌頂的牌從牌堆中取走。求取走的n張牌在原牌堆中的位置。
小筆記:簡單模擬
#include <cstdio> #include <queue> using namespace std; int main() { int t; scanf("%d", &t); while (t--) { queue<int> Q; int a[15]; //記錄每張牌在原佇列中的位置 int k = 1; //記錄取牌順序 int n; scanf("%d", &n); for (int i = 1; i <= n; i++) Q.push(i); //將所有牌入佇列 for (int i = 1; i <= n; i++) { for (int j = 1; j <= i; j++) { Q.push(Q.front()); Q.pop(); } a[Q.front()] = k++; Q.pop(); } for (int i = 1; i <= n; i++) printf("%d ", a[i]); printf("\n"); } return 0; }
3.2.2 Card Stacking (3629)
題意:N個玩家圍成一圈打牌,牌堆共有K張牌(K為N的倍數),其中包含M=K/N張好牌和K-M張壞牌。然後和上一題差不多。
小筆記:模擬,發到手裡的牌直接出列,其他牌再次入列,發到發牌人手中的牌記錄這種牌的最初編號。
#include <cstdio> #include <queue> #include <algorithm> using namespace std; int main() { queue<int> Q; int a[50005]; //記錄好牌的編號 int n, k, p; scanf("%d%d%d", &n, &k, &p); for (int i = 1; i <= k; i++) Q.push(i); int m; for (m = 0; m < k / n; m++) for (int i = 1; i <= n; i++) { if (i == n) a[m] = Q.front(); Q.pop(); for (int j = 0; j < p; j++) { Q.push(Q.front()); Q.pop(); } } sort(a, a + m); for (int i = 0; i < m; i++) printf("%d\n", a[i]); return 0; }
3.2.3 Printer Queue (3125)
題意:一個佇列包含n個列印作業,每個作業有一個優先順序(1~9),從佇列頭開始,如果該作業的優先順序比佇列中其他所有作業的優先順序高,則列印該作業並出列,否則將該作業放入佇列尾;列印作業花費時間為1,其他操作不花費時間。求一個佇列中給定作業m,等待到列印完該作業共需要多長時間。
小筆記:模擬,每次取佇列頭,檢查佇列中是否有比它優先順序高的作業,有的話將該作業放入佇列尾,否則時間加1後出佇列。處理到指定作業m的時候,如果佇列中沒有比它優先順序高的作業,則時間加1,列印該作業;否則將它移到隊尾,繼續判斷下一個作業。
#include <cstdio> #include <vector> using namespace std; struct job { int v; //作業的序號 int p; //作業的優先順序 job(int a, int b) : v(a), p(b) {} }; //檢查佇列Q中是否有優先順序比x高的項,如果有,輸出真,否則輸出假 bool check(vector<job> Q, int x, unsigned int i) { while (i < Q.size()) if (x < Q[i++].p) return true; return false; } int main() { int t; scanf("%d", &t); while (t--) { //使用STL中的向量vector 實現佇列,利用其push_back操作向佇列尾部加入資料 vector<job> Q; int n, m; scanf("%d%d", &n, &m); for (int i = 0, p; i < n; i++) { scanf("%d", &p); Q.push_back(job(i, p)); } int ans = 1; for (int k = 0; check(Q, Q[k].p, k) || Q[k].v != m; k++) { if (check(Q, Q[k].p, k)) Q.push_back(Q[k]); else ans++; } printf("%d\n", ans); } return 0; }
3.2.4 Team Queue (2259)
題意:共有t組元素進行佇列操作。
入列操作:元素進入佇列之前先從頭檢查佇列中是否有同組的元素,如果有,直接排到同組元素後面,否則排到佇列尾。
出列操作:直接取出佇列頭的元素。
小筆記:按照題意進行佇列模擬,一共有 t 組佇列Q[1…t], x 所在的組為T[x],用一個佇列TQ記錄當前元素所在組的資訊。
#include <cstdio> #include <queue> using namespace std; const int N = 1001; int T[1000000]; //元素與佇列號的對映 int main() { int t, k = 0; while (scanf("%d", &t) && t) { printf("Scenario #%d\n", ++k); bool v[N]; //標識是否有同組元素 for (int i = 0; i < t; i++) { int n; scanf("%d", &n); v[i] = false; while (n--) { int x; scanf("%d", &x); T[x] = i; } } queue<int> Q[N]; //儲存每個組 queue<int> TQ; //儲存組號 char cmd[10]; while (scanf("%s", cmd) && cmd[0] != 'S') { if (cmd[0] == 'E') { int x; scanf("%d", &x); int i = T[x]; if (!v[i]) { v[i] = true; TQ.push(i); //將元素x所在組號T[x]放入佇列TQ } Q[i].push(x); // 將元素x放入佇列Q[T[x]] } else if (cmd[0] == 'D') { int i = TQ.front(); //從TQ佇列頭找到組號i printf("%d\n", Q[i].front()); //輸出Q[i]佇列頭元素 Q[i].pop(); if (Q[i].empty()) { TQ.pop(); v[i] = false; } } } printf("\n"); } return 0; }
3.2.5 Sliding Window (2823)
題意:陣列a[1…n]有n個數字,依次計算a[1…k]到A[n-k+1…n]區間的最大值和最小值。
小筆記:這道題特別重要,十分精華。一種方法是滑動視窗、單調佇列的思想應用,一定要掌握。到後面我們還會學習線段樹,也是對這種反覆問詢區間問題的殺招。
#include <cstdio> #include <queue> using namespace std; int a[1000005]; int main() { int n, k; scanf("%d%d", &n, &k); //求區間最小值 deque<int> minQ; for (int i = 1; i <= n; i++) { scanf("%d", &a[i]); while (!minQ.empty() && a[i] < a[minQ.back()]) minQ.pop_back(); minQ.push_back(i); if (minQ.front() < i - k + 1) minQ.pop_front(); if (i >= k) printf("%d ", a[minQ.front()]); } printf("\n"); //求區間最大值 deque<int> maxQ; for (int i = 1; i <= n; i++) { while (!maxQ.empty() && a[i] > a[maxQ.back()]) maxQ.pop_back(); maxQ.push_back(i); if (maxQ.front() < i - k + 1) maxQ.pop_front(); if (i >= k) printf("%d ", a[maxQ.front()]); } printf("\n"); return 0; }