洛谷 - P1886 滑动窗口(单调队列/线段树)
題目鏈接:點擊查看
題目大意:給出一個由n個數構成的序列,再給出一個長度為k的窗口,這個窗口從第一個下標開始一直向后移動,每次移動一個單位,每次移動詢問一次該窗口中的最大值和最小值,最后輸出答案
題目分析:看似是一個模擬,其實暴力模擬肯定會爆,因為涉及到區間最值問題,而且還是靜態的詢問,所以這個題目有三種方法可以解決,分別是st表,線段樹和單調隊列,因為st表我不會優化,所以有五個測試點MLE了,這里就不用那個方法了,線段樹的話空間復雜度比起st表一般是能小上5倍左右,但時間復雜度每一次查詢時要多logn的常數,所以酌情考慮吧,用線段樹的話就不會MLE了,擦邊把這個題目給A了,一會也放一下代碼,沒什么好說的,主要是來說一下單調隊列來做這個題目,之前只是學過單調棧,今天接觸了一下單調隊列,發現還是挺簡單的,主要原理就是用雙端隊列維護一個嚴格遞增或嚴格遞減的序列,每次增加一個新值的時候,將其與尾部比較,最終插入尾部,當使用的時候,是使用頭部的數值,大概就是這樣了,一會看代碼理解一下吧
然后這個題目還需要維護一下每個數值所代表的id,用來判斷該數值在當前區間中是否過期,若過期的話直接舍棄掉即可
當然三種方法的復雜度來比較一下吧:
st表:空間復雜度nlogn,時間復雜度:打表nlogn,查詢O(1)
線段樹:空間復雜度n*4,時間復雜度:建樹nlogn,查詢nlogn
單調隊列:空間復雜度n,時間復雜度:n
吐槽一句:這個題目本來是poj上的題目,為什么跑來洛谷做呢,三種方法,st表MLE,線段樹TLE,單調隊列還不支持C++11,我:????
代碼:
單調隊列(deque):
#include<iostream> #include<cstdlib> #include<string> #include<cstring> #include<cstdio> #include<algorithm> #include<climits> #include<cmath> #include<cctype> #include<stack> #include<queue> #include<list> #include<vector> #include<set> #include<map> #include<sstream> #include<deque> using namespace std;typedef long long LL;const int inf=0x3f3f3f3f;const int N=1e6+100;struct Node {int val,id; }a[N];int ans_max[N],ans_min[N];int main() { // freopen("input.txt","r",stdin); // ios::sync_with_stdio(false);int n,k;while(scanf("%d%d",&n,&k)!=EOF){for(int i=1;i<=n;i++){scanf("%d",&a[i].val);a[i].id=i;}deque<Node>mmax;//維護最大值的單調隊列deque<Node>mmin;//維護最小值的單調隊列for(int i=1;i<=k-1;i++)//預處理1~k-1的值{while(mmax.size()&&mmax.back().val<=a[i].val)//將比當前值小的數全刪掉(尾部)mmax.pop_back();mmax.push_back(a[i]);while(mmin.size()&&mmin.back().val>=a[i].val)//將比當前值大的數全刪掉(尾部)mmin.pop_back();mmin.push_back(a[i]);}for(int i=k;i<=n;i++)//枚舉滑動窗口的末尾位置{int left=i-k+1;//記錄滑動窗口的首位置while(mmax.size()&&mmax.back().val<=a[i].val)//將比當前值小的數全刪掉(尾部)mmax.pop_back();mmax.push_back(a[i]);while(mmin.size()&&mmin.back().val>=a[i].val)//將比當前值大的數全刪掉(尾部)mmin.pop_back();mmin.push_back(a[i]);while(mmax.size()&&mmax.front().id<left)//將過期的值刪掉(頭部)mmax.pop_front();while(mmin.size()&&mmin.front().id<left)//將過期的值刪掉(頭部)mmin.pop_front();ans_max[left]=mmax.front().val;//更新答案ans_min[left]=mmin.front().val;}printf("%d",ans_min[1]);for(int i=2;i+k-1<=n;i++)printf(" %d",ans_min[i]);printf("\n%d",ans_max[1]);for(int i=2;i+k-1<=n;i++)printf(" %d",ans_max[i]);printf("\n");}return 0; }單調隊列(模擬):專門給poj2823改的代碼
#include<iostream> #include<cstdlib> #include<string> #include<cstring> #include<cstdio> #include<algorithm> #include<climits> #include<cmath> #include<cctype> #include<stack> #include<list> #include<vector> #include<set> #include<map> #include<sstream> using namespace std;typedef long long LL;const int inf=0x3f3f3f3f;const int N=1e6+100;struct Node {int val,id; }t[N];int a[N];int main() { // freopen("input.txt","r",stdin); // ios::sync_with_stdio(false);int n,k;while(scanf("%d%d",&n,&k)!=EOF){for(int i=1;i<=n;i++)scanf("%d",a+i);int l=1,r=0;for(int i=1;i<=n;i++){int left=i-k+1;while(l<=r&&t[l].id<left)l++;while(l<=r&&t[r].val>=a[i])r--;t[++r].val=a[i];t[r].id=i;if(i>=k)printf("%d ",t[l].val);}printf("\n");l=1,r=0;for(int i=1;i<=n;i++){int left=i-k+1;while(l<=r&&t[l].id<left)l++;while(l<=r&&t[r].val<=a[i])r--;t[++r].val=a[i];t[r].id=i;if(i>=k)printf("%d ",t[l].val);}printf("\n");}return 0; }線段樹:
#include<iostream> #include<cstdlib> #include<string> #include<cstring> #include<cstdio> #include<algorithm> #include<climits> #include<cmath> #include<cctype> #include<stack> #include<queue> #include<list> #include<vector> #include<set> #include<map> #include<sstream> using namespace std;typedef long long LL;const int inf=0x3f3f3f3f;const int N=1e6+100;int ans1[N],ans2[N];struct Node {int l,r,mmax,mmin; }tree[N<<2];void build(int k,int l,int r) {tree[k].l=l;tree[k].r=r;if(l==r){scanf("%d",&tree[k].mmin);tree[k].mmax=tree[k].mmin;return;}int mid=l+r>>1;build(k<<1,l,mid);build(k<<1|1,mid+1,r);tree[k].mmax=max(tree[k<<1].mmax,tree[k<<1|1].mmax);tree[k].mmin=min(tree[k<<1].mmin,tree[k<<1|1].mmin); }int query_max(int k,int l,int r) {if(tree[k].r<l||tree[k].l>r)return -inf;if(tree[k].r<=r&&tree[k].l>=l)return tree[k].mmax;return max(query_max(k<<1,l,r),query_max(k<<1|1,l,r)); }int query_min(int k,int l,int r) {if(tree[k].r<l||tree[k].l>r)return inf;if(tree[k].r<=r&&tree[k].l>=l)return tree[k].mmin;return min(query_min(k<<1,l,r),query_min(k<<1|1,l,r)); }int main() { // freopen("input.txt","r",stdin); // ios::sync_with_stdio(false);int n,k;while(scanf("%d%d",&n,&k)!=EOF){build(1,1,n);for(int i=1;i+k-1<=n;i++){ans1[i]=query_min(1,i,i+k-1);ans2[i]=query_max(1,i,i+k-1);}printf("%d",ans1[1]);for(int i=2;i+k-1<=n;i++)printf(" %d",ans1[i]);printf("\n%d",ans2[1]);for(int i=2;i+k-1<=n;i++)printf(" %d",ans2[i]);printf("\n");}return 0; }?
總結
以上是生活随笔為你收集整理的洛谷 - P1886 滑动窗口(单调队列/线段树)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: CodeForces - 622C N
- 下一篇: 洛谷 - P1725 琪露诺(动态规划+