• 单调栈&&单调队列


     

    单调栈:



    Largest Rectangle in a Histogram

     POJ - 2559 

    题意:给一个柱形图,问最大矩形面积是多少。

    方法一:

    用结构体存矩形的宽和高。

    从左到右扫,如果新来的矩形高小于等于栈顶的矩形,就弹出,并计算更新面积,把栈顶的矩形宽度累加到栈里的下一个矩形。

     1 #include <iostream>
     2 #include <cstdio>
     3 using namespace std;
     4 #define ll long long
     5 const int maxn=100010;
     6 ll top;
     7 ll ans;
     8 struct Node{
     9     ll h,w;
    10 }node;
    11 Node s[maxn];
    12 int main(){
    13     ll n;
    14     while(scanf("%lld",&n)&&n){
    15             ans=0;
    16             top=0;
    17         for(int i=0;i<n;i++){
    18             ll x;
    19             scanf("%lld",&x);
    20             node.h=x;
    21             node.w=1;
    22             ll w=0;
    23             while(top&&s[top].h>=x){
    24                 Node a=s[top--];
    25                 ll temp=a.h*(a.w+w);
    26                 w+=a.w;
    27                 if(temp>ans) ans=temp;
    28             }
    29             node.w+=w;
    30             s[++top]=node;
    31         }
    32         ll w=0;
    33         while(top){
    34             Node a=s[top--];
    35             ll temp=a.h*1LL*(a.w+w);
    36             w+=a.w;
    37             if(ans<temp) ans=temp;
    38         }
    39         printf("%lld
    ",ans);
    40     }
    41 }
    View Code

    方法二: 

    扫一遍得到每个矩形左右两边第一个比它低的矩形!!

    然后直接计算更新结果。

     1 #include <cstdio>
     2 #include <iostream>
     3 #include <cstring>
     4 #include <algorithm>
     5 using namespace std;
     6 #define ll long long
     7 const int maxn=100010;
     8 int s[maxn],h[maxn],L[maxn],R[maxn];
     9 int top;
    10 
    11 int main(){
    12     int n;
    13     while(scanf("%d",&n)&&n){
    14         top=0;
    15         s[top++]=0;
    16         h[n+1]=0;
    17         for(int i=1;i<=n;i++)  scanf("%d",&h[i]);
    18         for(int i=1;i<=n+1;i++){
    19             while(h[s[top-1]]>h[i]){
    20                 R[s[top-1]]=i;
    21                 top--;
    22             }
    23             L[i]=s[top-1];
    24             s[top++]=i;
    25         }
    26         ll ans=0;
    27         for(int i=1;i<=n;i++)
    28             ans=max(ans,1LL*(R[i]-L[i]-1)*h[i]);
    29         printf("%lld
    ",ans);
    30     }
    31     return 0;
    32 }
    View Code

    City Game

     POJ - 1964

    这个和上一题差不多,多了一维,可以用上一题的思路想。

    从上到下一行一行的来计算,每到达一行,计算当前各列的高度,然后维护单调栈来计算当前每一列的左右第一个比它低的列。再计算面积更新。

     1 #include <cstdio>
     2 #include <cstring>
     3 #include <iostream>
     4 using namespace std;
     5 const int maxn=1010;
     6 int p[maxn][maxn];
     7 int L[maxn],R[maxn],H[maxn];
     8 int s[maxn],top;
     9 
    10 int main(){
    11     int t;
    12     scanf("%d",&t);
    13     while(t--){
    14         int n,m;
    15         memset(H,0,sizeof(H));  //又是因为忘记初始化WA了。。。
    16         scanf("%d%d",&n,&m);
    17         for(int i=1;i<=n;i++)
    18         for(int j=1;j<=m;j++){
    19             char c=getchar();
    20             while(c!='F'&&c!='R') c=getchar();
    21             p[i][j]=c=='F'?1:0;
    22         }
    23         int ans=0;
    24         for(int i=1;i<=n;i++){
    25             for(int j=1;j<=m;j++){
    26                 if(p[i][j]) H[j]++;
    27                 else H[j]=0;
    28             }
    29             top=0;
    30             s[top++]=0;
    31             H[0]=-1;H[m+1]=-1;
    32             for(int j=1;j<=m+1;j++){
    33                 while(top&&H[s[top-1]]>H[j]){
    34                     R[s[top-1]]=j;
    35                     top--;
    36                 }
    37                 L[j]=s[top-1];
    38                 s[top++]=j;
    39             }
    40             for(int j=1;j<=m;j++){
    41                 ans=max(ans,H[j]*(R[j]-L[j]-1));
    42             }
    43         }
    44         printf("%d
    ",ans*3);
    45     }
    46     return 0;
    47 }
    View Code

    Bad Hair Day

     POJ - 3250 

    题意:从左往右,当前的牛可以看到第一头比它高的牛之前的所有牛,记为c[i],c[i]求和。

    考虑当前牛能被多少牛看见,维护一个单调递减栈,那么每到一头牛,栈里的牛都可以看到它。

     1 #include<cstdio>
     2 const int N=80000;
     3 int n,x,s[N],top;
     4 long long ans;
     5 int main()
     6 {
     7     scanf("%d",&n);
     8     for(int i=1;i<=n;i++)
     9     {
    10         scanf("%d",&x);
    11         while(top&&s[top]<=x)top--;
    12         ans+=top;//栈底从1开始,这样top就是元素,很方便
    13         s[++top]=x;
    14     }
    15     printf("%lld",ans);
    16     return 0;
    17 }
    View Code

    Feel Good

     POJ - 2796

    题意:给一个数组,定义s为某连续区间的长度乘以当前区间的最小值,求s的最大值。

    其实和第一题基本一样。

    先从两边各扫一遍找到每一个元素的左边界和右边界,使得它在这个区间内最小。

    然后扫一遍更新最大值即可。

     1 #include <cstdio>
     2 #include <cstring>
     3 #include <iostream>
     4 using namespace std;
     5 const int maxn=100010;
     6 int a[maxn],L[maxn],R[maxn];
     7 long long s[maxn];
     8 
     9 int main(){
    10     int n;
    11     scanf("%d",&n);
    12     for(int i=1;i<=n;i++){
    13         scanf("%d",&a[i]);
    14         L[i]=R[i]=i;
    15         s[i]=s[i-1]+a[i];
    16     }
    17     a[0]=a[n+1]=-1;
    18     for(int i=1;i<=n;i++){
    19         while(a[i]<=a[L[i]-1]) L[i]=L[L[i]-1];
    20     }
    21     for(int i=n;i>0;i--){
    22         while(a[i]<=a[R[i]+1]) R[i]=R[R[i]+1];
    23     }
    24     long long ans=-1;
    25     int l,r;
    26     for(int i=1;i<=n;i++){
    27         long long temp=(s[R[i]]-s[L[i]-1])*1ll*a[i];
    28         if(ans<temp){
    29             ans=temp;
    30             l=L[i];
    31             r=R[i];
    32         }
    33     }
    34     printf("%lld
    %d %d
    ",ans,l,r);
    35 }
    View Code
     1 #include <cstdio>
     2 #include <cstring>
     3 #include <iostream>
     4 #define ll long long
     5 using namespace std;
     6 const int maxn=100010;
     7 int s[maxn],top;
     8 int a[maxn],L[maxn],R[maxn];
     9 ll sum[maxn];
    10 int n;
    11 int main(){
    12     while(scanf("%d",&n)!=EOF){
    13         for(int i=1;i<=n;i++) {
    14             scanf("%d",&a[i]);
    15             sum[i]=sum[i-1]+a[i];
    16         }
    17         a[n+1]=-1;
    18         top=0;
    19         s[top++]=0;
    20         for(int i=1;i<=n+1;i++){
    21             while(top&&a[s[top-1]]>a[i]){
    22                 R[s[top-1]]=i;
    23                 top--;
    24             }
    25             L[i]=s[top-1];
    26             s[top++]=i;
    27         }
    28       //  for(int i=1;i<=n;i++){
    29          //   printf("%d %d
    ",L[i],R[i]);
    30       //  }
    31         ll ans=-1;
    32         int l,r;
    33         for(int i=1;i<=n;i++){
    34             ll temp=(sum[R[i]-1]-sum[L[i]])*a[i];
    35             if(ans<temp){
    36                 l=L[i]+1;
    37                 r=R[i]-1;
    38                 ans=temp;
    39             }
    40         }
    41         printf("%lld
    %d %d
    ",ans,l,r);
    42     }
    43 }
    View Code

    Terrible Sets

     POJ - 2082 

    题意:还是求最大矩形面积。。。

    我怎么做的单调栈都是这个题。。。

     1 #include <cstdio>
     2 #include <cstring>
     3 #include <iostream>
     4 using namespace std;
     5 const int maxn=50010;
     6 
     7 int s[maxn],top;
     8 int sumw[maxn];
     9 int w[maxn],h[maxn];
    10 
    11 int main(){
    12     int n;
    13     while(scanf("%d",&n)&&n!=-1){
    14         for(int i=1;i<=n;i++){
    15             scanf("%d%d",&w[i],&h[i]);
    16             sumw[i]=sumw[i-1]+w[i];
    17         }
    18         top=0;
    19         h[n+1]=-1;
    20         s[top++]=0;
    21         int ans=-1;
    22         for(int i=1;i<=n+1;i++){
    23             while(top&&h[i]<h[s[top-1]]){
    24                 int temp=(sumw[i-1]-sumw[s[top-2]])*h[s[top-1]];
    25                 ans=max(ans,temp);
    26                 top--;
    27             }
    28             s[top++]=i;
    29         }
    30         printf("%d
    ",ans);
    31     }
    32 }
    View Code

    单调队列:



    Sliding Window

     POJ - 2823 

    题意:求长度为k的窗口里的最大值和最小值。

    以最大值为例:

    从左往右扫,

    如果当前窗口前面的元素超出窗口,弹出

    如果新扫到的元素大于区间末尾的元素,则弹出队尾,因为后面的元素比它更优,它不可能是答案。

     1 #include <cstdio>
     2 #include <iostream>
     3 #include <cstring>
     4 using namespace std;
     5 
     6 const int maxn=1e6+10;
     7 int head,tail;
     8 int que[maxn];
     9 int a[maxn],n,k;
    10 
    11 int main(){
    12     while(scanf("%d%d",&n,&k)!=EOF){
    13         for(int i=1;i<=n;i++) scanf("%d",&a[i]);
    14         head=tail=0;
    15         for(int i=1;i<=n;i++){
    16             while(head!=tail&&que[head]<=i-k) head++;             //不在查询区间内,出队
    17             while(head!=tail&&a[que[tail-1]]>=a[i]) tail--;
    18             que[tail++]=i;
    19             if(i>=k) printf("%d%c",a[que[head]],i==n?'
    ':' ');
    20         }
    21         head=tail=0;
    22         for(int i=1;i<=n;i++){
    23             while(head!=tail&&que[head]<=i-k) head++;
    24             while(head!=tail&&a[que[tail-1]]<=a[i]) tail--;
    25             que[tail++]=i;
    26             if(i>=k) printf("%d%c",a[que[head]],i==n?'
    ':' ');
    27         }
    28     }
    29     return 0;
    30 }
    View Code
  • 相关阅读:
    Python GIL-------全局解释器锁
    JavaScript简介
    MongoDB查询
    创建、更新和删除文档
    MongoDB基础知识
    Linux安装mysql
    函数、变量、参数
    循环语句
    控制语句
    集合
  • 原文地址:https://www.cnblogs.com/yijiull/p/7297949.html
Copyright © 2020-2023  润新知