單調隊列複習

單調隊列

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;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章