POJ2082 Terrible Sets [DP 单调栈]

题意:

能把题目写得这么纠结真是种境界。

本质即是给定一定顺序的矩形,每个矩形都紧挨着x轴,问最大的矩形面积是?

思路:

1.

O(n^2)的暴力法:

#include<iostream>
#include<string>
#include<cmath>
#include<algorithm>
#define max(a,b) (a>b?a:b)
#define abs(a) ((a)>0?(a):-(a))
#define min(a,b) (a<b?a:b)
using namespace std;
const int N=50005;
int n;
int d[N];
struct
{
	int w,h;
}rect[N];
int main()
{
	while(scanf("%d",&n),n!=-1)
	{
		int ans=0;
		for(int i=1;i<=n;i++)
		{
			scanf("%d%d",&rect[i].w,&rect[i].h);
		}
		for(int i=1;i<=n;i++)
		{
			int sumw=0;
			for(int j=i;j>=1;j--)
			{
				if(rect[j].h>=rect[i].h)
				{
					sumw+=rect[j].w;
				}
				else
					break;
			}
			for(int j=i+1;j<=n;j++)
			{
				if(rect[j].h>=rect[i].h)
				{
					sumw+=rect[j].w;
				}
				else
					break;
			}
			ans=max(ans,(sumw*rect[i].h));
		}
		printf("%d\n",ans);
	}
    return 0;
}


2.

O(n)的算法:

首先是要注意到,当矩形依次是高度是升序排列的时候。

只要从右到左,即从大到小遍历统计一遍O(n)的时间即可算出最大值。

因为最大值要么是某个矩形单独形成的面积,要么是某个矩形和它左边(即比它小)的连续若干个矩形形成的。


从中我们看到O(n)算法的希望,因为一行矩形可以分成是若干行从小到大的顺序排列的矩形。

注意到,如果设定小到大是顺序,则如果遇到第i个矩形,比第i-1个矩形小,即出现逆序矩形的时候。

我们不能忍。

所以必须将其严办:

合并!

将其和前面的若干个矩形(从j=i-1一直j--搜索到某个j比现在这个i的高度低)合并。

整个过程是用栈来模拟,栈里面是存放顺序的新矩形,当遇到逆序的矩形时要合并:

将高度比新矩阵高的矩形都出栈了,(因为这些矩形是顺序的,所以按照之前的算法出栈的同时即可统计这些矩形最大面积mx)。

然后将出栈的矩形的总宽度sumw加在新矩形的宽度上。

此时新矩形宽度是原宽度+sumw,高度是原高度。

将这个矩形入栈。

最后遍历结束后最后再进行一次出栈操作。


#include<iostream>
#include<string>
#include<cmath>
#include<algorithm>
#define max(a,b) (a>b?a:b)
#define abs(a) ((a)>0?(a):-(a))
#define min(a,b) (a<b?a:b)
using namespace std;
const int N=50005;
int n;
struct
{
	int w,h;
}stack[N];
int sp;
int main()
{
	while(scanf("%d",&n),n!=-1)
	{
		int ans=0;
		int a,b;
		for(int i=1;i<=n;i++)
		{	
			scanf("%d%d",&a,&b);
			if(b>=stack[sp].h)
			{
				stack[++sp].w=a;
				stack[sp].h=b;
			}
			else
			{
				int sumw=0;
				while(stack[sp].h>=b)
				{
					sumw+=stack[sp].w;
					ans=max(ans,sumw*stack[sp].h);
					sp--;
				}
				sumw+=a;
				stack[++sp].w=sumw;
				stack[sp].h=b;
			}
		}
		int sumw=0;
		while(sp>0)//最后栈里如果有元素则需要清空一遍
		{
			sumw+=stack[sp].w;
			ans=max(ans,sumw*stack[sp].h);
			sp--;
		}
		printf("%d\n",ans);
	}
    return 0;
}

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值