CSP-training Week5 Problem B 單調隊列問題(POJ - 2823 )

Week5 Problem B 單調隊列問題

數據結構概述

隊列,一種常見的數據結構,他模擬了我們日常生活中的隊列數據結構,具有實際生活中隊列的特點:先來先進入隊列,隊列的前段可以先接受服務。作爲一種常用且基礎的數據結構,c++的STL庫也提供了封裝好的隊列結構,可以使用下面的語句來進行封裝類型queue的使用

#include<queue>

關於STL中隊列的具體方法,可以查詢c++官方文檔進行學習:c++官方文檔
在題目中將使用一種隊列的延伸數據結構–單調數據結構,並用他來解決一定區域內進行最大最小值搜索等數值比較問題。

題目概述

題目敘述

ZJM 有一個長度爲 n 的數列和一個大小爲 k 的窗口, 窗口可以在數列上來回移動. 現在 ZJM 想知道在窗口從左往右滑的時候,每次窗口內數的最大值和最小值分別是多少. 例如:
數列是 [1 3 -1 -3 5 3 6 7], 其中 k 等於 3.

窗口狀態 最小值 &最大值
[1 3 -1] -3 5 3 6 7 -1 3
1 [3 -1 -3] 5 3 6 7 -3 3
1 3 [-1 -3 5] 3 6 7 -3 5
1 3 -1 [-3 5 3] 6 7 -3 5
1 3 -1 -3 [5 3 6] 7 3 6
1 3 -1 -3 5 [3 6 7] 3 7

INPUT & 輸入樣例

輸入有兩行。第一行兩個整數n和k分別表示數列的長度和滑動窗口的大小,1<=k<=n<=1000000。第二行有n個整數表示ZJM的數列。
輸入樣例:

8 3
1 3 -1 -3 5 3 6 7

OUTPUT & 輸出樣例

輸出有兩行。第一行輸出滑動窗口在從左到右的每個位置時,滑動窗口中的最小值。第二行是最大值。
輸出樣例:

-1 -3 -3 -3 3 3
3 3 5 5 6 7

題目重述和坑點

將題意簡述可以縮略成下列要求:給定一個數值序列,和一個窗口的大小,將窗口以step=1的速度向後移動,計算出每個窗口狀態下,窗口內的最大值和最小值。

解題思路

如果你熟悉單調隊列,應該會發現這道題在明白題意後是一道簡單明瞭的單調隊列板子題,因爲他有下述幾個關鍵字:
1、一個數值的序列,且順序訪問。
2、要搜索最大值、最小值的範圍是一個局部區域(窗口)
3、要處理的數據比較簡單,能夠直接比較大小

爲什麼要用單調隊列:

綜合題意可知,該問題的主要要求有兩個:求出最大(最小)值&維持窗口大小,保持單調性我們可以採取類似於單調棧的解法:維持一個單調遞增(遞減)序列,通過插入彈出的方法維持數值的有序,具體內容可以見:單調棧內容詳解
但是另一個要求是我們已有的數據結構無法滿足的:保持窗口的大小不變。

單調隊列具體操作

因此我們引入單調隊列(單調且可以從頭部、尾部進行操作)
要求1解決方案:單調性的維持,我們可以將單調棧的維持方法搬過來放在隊列尾部的維持,即可維護隊列始終保持單調
要求2解決方案:要維護一個隊列中存放的值不超出窗口,可以使用下標隊列來進行標定,在隊列中存放每個元素的真實數組下標,一旦存在
隊尾新插入元素下標-隊首元素下標>=窗口大小
證明窗口移動後,隊首的元素已經不屬於當前窗口,可以彈出。
通過如上維持單調隊列的做法,我們可以保證隊列的隊首始終是當前窗口合法的(符合窗口大小的)最大值(最小值),問題得以解決。

總結

一道單調隊列板子題,但是通過這道題可以很好得學習單調隊列的中心思想:
1、尾部維持單調性
2、頭部維持搜索區域的大小
單調隊列和單調棧常常混合出題,他們分別解決了在一個數值序列中進行局部搜索和全局搜索的問題,可以對比理解使用。

題目源碼

#include<iostream>
#include<cstdio>
using namespace std;
int q[1000010];
int a[1000010];
int max_num[1000010];
int min_num[1000010];
int number=0;
int win=0;
void make_max()//維持遞減數列
{
    int L=1;
    int R=0;
    for(int i=1;i<=number;i++)
    {
        while(R>=L && a[q[R]]<=a[i])
        {R--;}
        q[++R]=i;
        if(q[R]-q[L]>win-1)
        L++;
        max_num[i]=q[L];
    }
}
void make_min()//維持遞增數列數列
{
    int L=1;
    int R=0;
    for(int i=1;i<=number;i++)
    {
        while(R>=L && a[q[R]]>=a[i])
        {R--;}
        q[++R]=i;
        if(q[R]-q[L]>win-1)
        L++;
        min_num[i]=q[L];
    }
}
int main()
{
    scanf("%d %d",&number,&win);
    for(int i=1;i<=number;i++)
    {
        scanf("%d",&a[i]);
    }
    make_max();
    make_min();
    for(int i=win;i<=number;i++)
    {
        printf("%d ",a[min_num[i]]);
    }
    printf("\n");
    for(int i=win;i<=number;i++)
    {
        printf("%d ",a[max_num[i]]);
    }
    printf("\n");
    
    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章