1. 程式人生 > 實用技巧 >常用技巧:單調棧

常用技巧:單調棧

通過單調棧,可以將一些題目從O(n2)降到O(n),不需要列舉,掃描一遍將之前的結果存到棧中,處理當前位置時從棧中獲取之前的資訊,並將當前位置存入棧中。典型模型是求最大矩陣:POJ2559

#include<stdio.h>
#include<algorithm>
using namespace std;
typedef long long ll;
int n;
int h[100005],l[100005],r[100005],st[100005];
int main(){
    while(scanf("%d",&n)!=EOF){
        if(n==0) break;
        
for(int i=1;i<=n;i++) scanf("%d",&h[i]); int t=0; for(int i=1;i<=n;i++){ while(t>0&&h[i]<=h[st[t-1]]) t--; if(t==0) l[i]=1; else l[i]=st[t-1]+1; st[t++]=i; } t=0; for(int i=n;i>=1;i--){
while(t>0&&h[i]<=h[st[t-1]]) t--; if(t==0) r[i]=n+1; else r[i]=st[t-1]; st[t++]=i; } ll ans=0; for(int i=1;i<=n;i++){ ans=max(ans,(ll)h[i]*(r[i]-l[i])); } printf("%lld\n",ans); } }

POJ3494,求最大全1子矩陣。可以分行處理,根據當前位置上的情況,決定在當前位置的高度。h[j]=map[i][j]==0?0:h[j-1]+1,那麼就成為一個在當前行上求最大矩形面積的問題。類似壓行的思想還有求最大子矩陣和的問題,把每列上的數累積求和,在每行上解決最大子段和問題即可。

#include<stdio.h>
#include<algorithm>
using namespace std;
int m,n,num[2005][2005],h[2005],l[2005],r[2005],st[2005];
int main(){
    while(scanf("%d%d",&m,&n)!=EOF){
        for(int i=1;i<=m;i++){
            for(int j=1;j<=n;j++){
                scanf("%d",&num[i][j]);
            }
        }

        int ans=0;
        for(int i=1;i<=m;i++){
            for(int j=1;j<=n;j++){
                h[j]=num[i][j]==0?0:h[j]+1;
            }
            int t=0;
            for(int j=1;j<=n;j++){
                while(t>0&&h[j]<=h[st[t-1]]) t--;
                if(t==0) l[j]=1;
                else l[j]=st[t-1]+1;
                st[t++]=j;
            }
            t=0;
            for(int j=n;j>=1;j--){
                while(t>0&&h[j]<=h[st[t-1]]) t--;
                if(t==0) r[j]=n+1;
                else r[j]=st[t-1];
                st[t++]=j;
            }

            for(int j=1;j<=n;j++){
                ans=max(ans,h[j]*(r[j]-l[j]));
            }
        }
        printf("%d\n",ans);
    }
}