单调栈:
Largest Rectangle in a Histogram
题意:给一个柱形图,问最大矩形面积是多少。
方法一:
用结构体存矩形的宽和高。
从左到右扫,如果新来的矩形高小于等于栈顶的矩形,就弹出,并计算更新面积,把栈顶的矩形宽度累加到栈里的下一个矩形。
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 }
方法二:
扫一遍得到每个矩形左右两边第一个比它低的矩形!!
然后直接计算更新结果。
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 }
City Game
这个和上一题差不多,多了一维,可以用上一题的思路想。
从上到下一行一行的来计算,每到达一行,计算当前各列的高度,然后维护单调栈来计算当前每一列的左右第一个比它低的列。再计算面积更新。
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 }
Bad Hair Day
题意:从左往右,当前的牛可以看到第一头比它高的牛之前的所有牛,记为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 }
Feel Good
题意:给一个数组,定义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 }
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 }
Terrible Sets
题意:还是求最大矩形面积。。。
我怎么做的单调栈都是这个题。。。
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 }
单调队列:
Sliding Window
题意:求长度为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 }