【POJ 2823】 Sliding Window 單調隊列 雙端隊列 STL

Description

An array of size n ≤ 106 is given to you. There is a sliding window of size k which is moving from the very left of the array to the very right. You can only see the k numbers in the window. Each time the sliding window moves rightwards by one position. Following is an example:
The array is [1 3 -1 -3 5 3 6 7], and k is 3.
Window position Minimum value Maximum value
[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
Your task is to determine the maximum and minimum values in the sliding window at each position.

Input

The input consists of two lines. The first line contains two integers n and k which are the lengths of the array and the sliding window. There are n integers in the second line.
Output

There are two lines in the output. The first line gives the minimum values in the window at each position, from left to right, respectively. The second line gives the maximum values.
Sample Input

8 3
1 3 -1 -3 5 3 6 7
Sample Output

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

題意:求從k位置開始,求每個位置的【i-k+1,i】區間內的最大最小值

思路(單調隊列) :

按照題意,要求每個位置的前k個元素內的最大最小值,那就要不斷地更新這k個元素,並滿足先進先出的關係(k個之前的元素都要淘汰),所以會想到藉助隊列。那麼這個隊列應該要滿足怎麼樣的性質才能對答案有針對性呢?
我們想構造這樣一個隊列,使得取最大值的時候,取出隊頭就可以( O(1)) ,同時,這個隊列應該滿足前面的元素都是下標比較小的,換句話說,隊列從頭到尾下標依次遞增(不一定連續),這樣就可以通過循環丟掉隊頭,使得隊內元素都在k範圍內。
這樣,不難想到,藉助一個STL的雙端隊列(比較懶就不自己數組模擬了),在取最大值的時候,構造一個單調遞減的隊列,每個a[i]如果比隊尾小,就直接push進隊尾,否則把前面比它小的都pop掉(從隊尾開始),因爲前面比它小的對i後面的取最大值沒有貢獻(我a[i]都比a[i-1]大了,最大值肯定不會是a[i-1]呀,所以這個位置丟掉)。 然後每次i處理完後就取一次隊頭,就是當前i的答案了。反之亦然

AC代碼:

#include<iostream>
#include<string>
#include<algorithm>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<map>
#include <queue>
#include<sstream>
#include <stack>
#include <set>
#include<vector>
#define FAST ios::sync_with_stdio(false)
#define abs(a) ((a)>=0?(a):-(a))
#define sz(x) ((int)(x).size())
#define all(x) (x).begin(),(x).end()
#define mem(a,b) memset(a,b,sizeof(a))
#define max(a,b) ((a)>(b)?(a):(b))
#define min(a,b) ((a)<(b)?(a):(b))
#define rep(i,a,n) for(int i=a;i<=n;++i)
#define per(i,n,a) for(int i=n;i>=a;--i)
#define pb push_back
#define mp make_pair
#define fi first
#define se second
using namespace std;
typedef pair<int,int> PII;
typedef long long ll;
const int maxn = 1e6+5;
const int inf=0x3f3f3f3f;
const double eps = 1e-7;
const double pi=acos(-1.0);
const int mod = 1e9+7;
inline int lowbit(int x){return x&(-x);}
ll gcd(ll a,ll b){return b?gcd(b,a%b):a;}
void ex_gcd(ll a,ll b,ll &d,ll &x,ll &y){if(!b){d=a,x=1,y=0;}else{ex_gcd(b,a%b,d,y,x);y-=x*(a/b);}}//x=(x%(b/d)+(b/d))%(b/d);
inline ll qpow(ll a,ll b,ll MOD=mod){ll res=1;a%=MOD;while(b>0){if(b&1)res=res*a%MOD;a=a*a%MOD;b>>=1;}return res;}
inline ll inv(ll x,ll p){return qpow(x,p-2,p);}
inline ll Jos(ll n,ll k,ll s=1){ll res=0;rep(i,1,n+1) res=(res+k)%i;return (res+s)%n;}
inline ll read(){ ll x = 0;char ch = getchar();while(ch>'9'||ch<'0') ch = getchar();while(ch>='0'&&ch<='9') x = (x<<3) + (x<<1) + ch - '0',  ch = getchar();return x; }
int dir[4][2] = { {1,0}, {-1,0},{0,1},{0,-1} };
ll a[maxn];
deque<ll> q1;
deque<ll> q2;
ll n; ll k;
void init()
{
    while(!q1.empty()) q1.pop_back();
    while(!q2.empty()) q2.pop_back();
}

void solve()
{
    rep(i,1,n)      //從小到大
    {
        if(q1.empty()||a[i]>a[q1.back()])       //如果隊空或者當前比隊尾要大,直接放進來
                q1.push_back(i);
        else            //否則
                {
                    while(q1.size()&& (a[q1.back()]>=a[i]||q1.back()+k-1<i))        //說明前面比它大的那些對後面的“前k個"沒有作用(作用被當前承包),pop掉
                    q1.pop_back();
                    q1.push_back(i);        //最後它前面要麼是空的要麼是比它小的
                }
       if(i>k)
        {
            while(q1.size()&&q1.front()<i-k+1)
            q1.pop_front();     //k+1位開始每次pop掉i-k之前的
            printf("%lld%c",a[q1.front()], i==n?'\n':' ');
        }
        else if (i==k)
        printf("%lld%c",a[q1.front()], i==n?'\n':' ');  //k的時候就輸出隊頭
    }
    while(!q1.empty()) q1.pop_back();
   rep(i,1,n)      //從大到小,取隊頭最大的
    {
        if(q1.empty()||a[i]<a[q1.back()])       //如果隊空或者當前比隊尾要小,直接放進來
                q1.push_back(i);
        else            //否則
                {
                    while(q1.size()&& (a[q1.back()]<=a[i]||q1.back()+k-1<i))        //說明前面比它小的那些對後面的“前k個最大"沒有作用(作用被當前承包),pop掉
                    q1.pop_back();
                    q1.push_back(i);        //最後它前面要麼是空的要麼是比它大的
                }
       if(i>k)
        {
            while(q1.size()&&q1.front()<i-k+1)
            q1.pop_front();     //k+1位開始每次pop掉i-k之前的
            printf("%lld%c",a[q1.front()], i==n?'\n':' ');
        }
        else if (i==k)
        printf("%lld%c",a[q1.front()], i==n?'\n':' ');  //k的時候就輸出隊頭
    }
}

int main()
{
    scanf("%lld%lld",&n,&k);
    k = min(k,n);
    rep(i,1,n) scanf("%lld",&a[i]);
    init();
    solve();
    return 0;
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章