单调栈&&单调队列

 

单调栈:



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
原文地址:https://www.cnblogs.com/yijiull/p/7297949.html