【BZOJ 1113】 [Poi2008]海报PLA

1113: [Poi2008]海报PLA

Time Limit: 10 Sec   Memory Limit: 162 MB
Submit: 751   Solved: 453
[ Submit][ Status]

Description

N个矩形,排成一排. 现在希望用尽量少的矩形海报Cover住它们.

Input

第一行给出数字N,代表有N个矩形.N在[1,250000] 下面N行,每行给出矩形的长与宽.其值在[1,1000000000]2 1/2 Postering

Output

最少数量的海报数.

Sample Input

5
1 2
1 3
2 2
2 5
1 4

Sample Output

4

RMQ/单调栈。


如果两个矩形能同时被一个海报覆盖,要满足两个条件:

1.他俩的高度相同


2.他们之间的海报没有比他俩更矮的


先说我的做法:

用RMQ预处理出区间最小值,然后依次枚举即可。


但要注意一个问题,如果有好多个矩形高度相同,那么需要从每一个为起点,来进行判断,可行则跳出,不可行则继续枚举。


好麻烦。。


其实正解是单调栈。。


维护一个单调递增的栈(因为如果当前高度比之前小,那么之前的永远不可能满足要求了);


如果遇到和队尾相同的,那么使答案减1。


下面是我的RMQ代码(无视掉Solve()。。那是我的对拍)


#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdlib>
#include <cstdio>
#define M 250005
using namespace std;
int f[M][20],h[M],n;
struct data
{
	int x,p;
}a[M];
void read(int &tmp)
{
	tmp=0;
	char ch=getchar();
	int fu=1;
	for (;ch<'0'||ch>'9';ch=getchar())
		if (ch=='-') fu=-1;
	for (;ch>='0'&&ch<='9';ch=getchar())
		tmp=tmp*10+ch-'0';
	tmp*=fu;
}
void RMQ()
{
	for (int i=1;i<=n;i++)
		f[i][0]=h[i];
	for (int j=1;(1<<j)<=n;j++)
		for (int i=1;i+(1<<j)-1<=n;i++)
			f[i][j]=min(f[i][j-1],f[i+(1<<(j-1))][j-1]);
}
int Getlog(int x)
{
	int now=1;
	for (int i=0;;i++)
	{
		now<<=1;
		if (now>=x) return i;
	}
}
int Getmin(int l,int r)
{
	int k=Getlog(r-l+1);
	return min(f[l][k],f[r-(1<<k)+1][k]);
}
int Solve(int l,int r)
{
	if (l==r) return 1;
	int ans=1;
	int m=Getmin(l,r);
	int cnt=0;
	for (int i=l;i<=r;i++)
		if (h[i]==m) cnt++;
	if (cnt==1) return r-l+1;
	int from=l;
	for (int i=l;i<=r;i++)
		if (h[i]==m)
		{
			if (from<i) ans+=Solve(from,i-1);
			from=i+1;
		}
	if (from<=r) ans+=Solve(from,r);
	return ans;
}
bool cmp(data a,data b)
{
	if (a.x==b.x) return a.p<b.p;
	return a.x<b.x;
}
int main()
{
	read(n);
	int x;
	for (int i=1;i<=n;i++)
		read(x),read(h[i]),a[i].x=h[i],a[i].p=i;
	RMQ();
	sort(a+1,a+1+n,cmp);
	int now=1;
	int ans=n;
	while (now<=n)
	{
		int r=now;
		while (r+1<=n&&a[r+1].x==a[r].x)
			r++;
		int k=now+1;
		for (int i=now;i<r;i++)
		{
			if (k<=i) k=i+1;
			int j;
			for (j=k;j<=r;j++)
			{
				if (Getmin(a[i].p,a[j].p)==a[i].x) ans--;
				else {k=j;break;}
			}
			if (j>r) break;
		}
		now=r+1;
	}
	cout<<ans<<endl;
	return 0;
}




hzwer的单调栈代码。

#include<iostream>
#include<cstdio>
using namespace std;
int t,x,n,s[250001],top,ans;
int main()
{
	scanf("%d",&n);
	for(int i=1;i<=n;i++)
	{
	   scanf("%d%d",&t,&x);
	   while(x<=s[top])
           {
           if(x==s[top])ans++;
           top--;
           }
	   s[++top]=x;
        }
        printf("%d",n-ans);
	return 0;
}

感悟:

单调栈写起来有简单速度又快。。关键是抓住两者之间不能有比他们矮的这个条件。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值