單調隊列
1.適用情況
需要快速求出區間最大/最小值的情況(常用於dp優化)/有單調的題
2.原理/思路
每次有元素進隊時,找到合適的隊尾接上,保證隊列單調性。(刪除後面不必要的一段)同時,維護可用區間,及時去頭。
3.性質
一般,在動態規劃的過程中,單調隊列中每個元素一般存儲的是兩個值:
1.在原數列中的位置(下標)
2.他在動態規劃中的狀態值(而單調隊列則保證這兩個值同時單調)
4.模板題
參考博客
有N個數(N<=100000) ,在連續M(M<=N)個數裏至少要有一個數被選擇,求選出來數的最小總和。
輸入
第一行兩個整數 N,M
接下來N行 Wi(Wi<=100) 表示第i個數 。
輸出
一個整數,最小總和 。
Sample Input
5 3
1
2
5
6
2
Sample Output
4
思路分析
狀態枚舉到i,當m=4時,需要在i-3到i-1中找到最小的F[j],那麼枚舉到i+1時,需要在i-2到i中找到最小的F[j]。要尋找最小值的區間向後移動一位,也就是F[i-m+1]的值被拋棄,F[i-1]的值被加入。這裏可以用單調隊列處理,F[i-1]是插隊的數據。F[i-1]有資格插隊是因爲它更優且更靠近i,比它更差的數將被它取代,保留那些數據沒有任何好處。而那些已經不再維護區間之外的就不必再對其進行維護,出隊即可。
#include<bits/stdc++.h>
using namespace std;
const int N=100000;
int n,m,head=1,tail=0,ans=2147483647;
int a[N+1],f[N+1],que[N+1];
int main(){
scanf("%d%d",&n,&m);
for (int i=1;i<=n;i++)
scanf("%d",&a[i]);
for (int i=1;i<=n;i++){
while(head<=tail&&f[i-1]<=f[que[tail]])tail--;//當F[i-1]比隊尾值更優時把隊尾值彈出
que[++tail]=i-1;//把F[i-1]插入,這裏插入下標而不插入值,便於從隊頭彈出
while(head<=tail&&que[head]<i-m)head++;//不屬於區間維護內的數彈出
f[i]=f[que[head]]+a[i];//狀態轉移
}
for(int i=n;i>n-m;i--)//求出答案
ans=min(ans,f[i]);
printf("%d\n",ans);
return 0;
}
5.序列_題解
問題描述
作爲一名火星人,你爲了佔領地球,需要想方設法使地球人失去信心。現在你獲得了一項能力,控制今後n天的天氣溫度,對於第i天,你能將溫度控制在[ai,bi]中任意一個數字,你的目的是使其中某段時間,溫度持續不下降,趁此來攻擊地球。現在問你最多可以使連續的多少天滿足溫度不下降。
輸入
第一行給出一個整數n,表示你能控制的天數。
接下來n行,第i行給出2個整數ai,bi,表示你能控制的天氣範圍。保證ai<=bi。
輸出
輸出一個整數,表示答案。
Sample Input
4
1 3
2 4
1 1
3 4
Sample Input
2
數據範圍
對於20%的數據 3<=n<=10;
對於40%的數據 3<=n<=3000;
對於60%的數據 3<=n<=100000;
對於100%的數據 3<=n<=1000000,1<=ai,bi<=100000。
思路分析
由溫度要不嚴格遞增想到單調隊列。
#include<bits/stdc++.h>
using namespace std;
int n,a[1000010],b[1000010],q[1000010],head=1,tail=1,ans;
inline int read(){
int x=0;char ch=getchar();
while(ch<'0'||ch>'9') ch=getchar();
while(ch>='0'&&ch<='9'){
x=(x<<3)+(x<<1)+ch-'0';
ch=getchar();
}
return x;
}
int main(){
for(int i=1;i<=n;++i)
a[i]=read(),b[i]=read();
q[1]=1;
int r=2;
for(int i=1;i<=n&&r<=n;i++){
while(head<=tail&&q[head]<i)head++;
if(r<=i)r=i+1,q[++tail]=i;
while(r<=n&&b[r]>=a[q[head]]){
while(head<=tail&&a[q[tail]]<a[r])tail--;
q[++tail]=r++;
}
ans=max(ans,r-i);
}
printf("%d\n",ans);
return 0;
}