1. 程式人生 > 其它 >基礎資料結構3.2——佇列

基礎資料結構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;
}