【问题描述】
现在有n个宽度为1的矩形按如下图(左边的)所示的方式排在了一起:
用肉眼容易看出,在左图内部面积最大的矩形如右图绿色部分所标注。
现在我们考虑将其中一些宽度为1的矩形取出,按照原顺序再次紧密地靠在一起,比如在原图中,如果我们取出从左往右数的第2、4、5、6个矩形,那么我们得到下面的左图,而该图形内部的最大矩形则如右图黄色部分所示。
现在,你的任务就是从原图中取出若干个宽度为1的矩形并使用上面的方式组成新的图形,使得新的图形中,内部的最大矩形面积最大。
【输入格式】
第一行为一个正整数T,表示数据组数。 接下来T组数据,每组数据第一行为一个正整数n,表示矩形个数,接下来一行n个正整数,第i个数hi表示图形中从左到右第i个矩形的高度。
【输出格式】
对每组数据输出一行一个正整数,表示最大的“最大矩形”面积。
【输入样例】
3
4
1 2 3 4
3
8 1 9
3
8 6 9
【输出样例】
6
16
18
【数据规模与约定】
对于30%的数据,有1<=n<=15,
对于60%的数据,有1<=n<=1000,
对于100%的数据,有1<=T<=10, 1<=n,hi<=100000,每个测试点n的和不超过200000。
题目分析
这是一个裸的单调栈,我转载了别人的代码如下。设第一个比当前数(序号为i)小的左侧数为left[i]。算法的流程是这样的:设现在程序循环到第i个数,如果当前,栈顶的数大于等于第i个数,说明什么?说明序号i以后的数j,它们的left[j]一定不会是栈顶那个数,因为选了j一定能选i。既然这样,直接把j从栈中弹出,然后继续判断……直至第i个数遇到比它小的栈顶的数。这个数就是left[i],因为比i小的数只会被更小更优的数替换。然后,将i入栈。这样就把那些无用的,值大于第i个数,却在第i个数左边的数统统用第i个数替换了。这就去除了冗余状态。
代码实现
#include<bits/stdc++.h> using namespace std; #define N 100001 #define int long long int s[N],a[N],w[N],T,n,top,ans; signed main(){ freopen("rectangle.in","r",stdin),freopen("rectangle.out","w",stdout); cin>>T; while (T--){ cin>>n; for (register int i=1;i<=n;++i) cin>>a[i]; sort(a+1,a+n+1); a[n+1]=top=ans=0; for (register int i=1;i<=n+1;++i) if (s[top]<a[i]&&top) s[++top]=a[i],w[top]=1; else{int wide=0; while(s[top]>=a[i]&&top){ wide+=w[top]; ans=max(ans,wide*s[top--]); }s[++top]=a[i],w[top]=wide+1; }cout<<ans<<"\n"; } return 0; }
代码分析
原来的HDU1506也是很水,这一题也是,唯一的区别在于排序了。我将会认真的讲解一下单调栈问题。
我们要维护的栈是一个单调递增的数列,为什么呢?我们的答案最好是用较高的答案加上去的,所以用递增数列。我们的答案矩阵是长乘宽,我们维护一个队列,当前的数字比栈顶还大,自然是递增的,入栈即可。如果小于等于,我们保存的是栈中比这个数字小(等于)的数字,当然这时候我们把宽度加一下啊。。。因为这时候我们维护的矩形可能是最大的,自然要记录了。我们一边出栈一边更新答案,虽然有可能不会更新掉答案。这里贼骚:原来的宽度是不是被pop掉了?我们再把宽度加回去哈。居然有这种操作,原因是原来的部分被算过一遍了,我们如果有更新ans就更新过了,我们可以把这个部分当做一个小矩形加入栈里面。如果最后还有答案怎么办啊?我们用一个高度为0的矩形入栈,然后你懂得。
当然如果你要压行,我不介意。本来要讲一讲我和小姐姐的故事,下次再说。