hihocoder #1273 清理海报 建图思想+dfs+状态压缩记录 好题!

31 篇文章 0 订阅
29 篇文章 0 订阅

描述

小Hi实验室所在的建筑一楼有一个用于贴海报的黑板,不停的有新的海报往上贴,也会安排人员不断的对海报进行清理,而最近,轮到了小Hi去对海报进行清理。

黑板是一块W*H大小的区域,如果以左下角为直角坐标系的话,在上次清理后第i张贴上去的海报可以视作左下角为(X1i, Y1i),右上角为(X2i, Y2i)的一个矩形。

撕去一张海报会导致所有覆盖在其上的海报都被同时撕掉(这样被称为连带,这个过程是具有传递性的,即如果A覆盖B,B覆盖C,那么撕掉C会导致A和B均被撕掉),但是一张海报想要被手动撕掉的话需要至少存在一个角没有被其他海报覆盖(海报A被海报B覆盖当且仅当他们存在面积大于0的交集并且A在B之前贴出,海报A的一个角被海报B覆盖当且仅当这个顶点处于海报B的内部)。

于是现在问题来了,为了节约时间,小Hi决定一次性撕掉尽可能多的海报,那么他应该选择哪张海报呢?在效果相同的情况下,小Hi倾向于选择更早贴出的海报。

输入

每个输入文件仅包含单组测试数据。

每组测试数据的第一行为三个正整数W,H和N,分别表示黑板的宽、高以及目前张贴出的海报数量。

接下来的N行,每行为四个正整数X1i、Y1i、X2i和Y2i,描述第i张贴出的海报。

对于20%的数据,满足1<=N<=5,1<=W,H<=10

对于100%的数据,满足1<=N<=1000,0<=X1i, X2i <= W, 0<=Y1i, Y2i<=H, 1<=W,H<=108

输出

对于每组测试数据,输出两个正整数Ans和K,表示小Hi一次最多能撕掉多少张海报,和他选择的海报是第几张贴出的。

样例输入
6 7 4
0 0 4 4
1 0 3 4
1 4 4 6
0 0 3 5
样例输出
3 1
这题我是一点没思路啊...虽然是个lv2的..就看了看讨论区,北大的那巨巨真猛...

大牛们的思路就是 把每一张海报都看成是一个点,如果pa被pb覆盖,即撕掉pa可以导致将pb撕掉,那么我们就使pa有一条指向pb的有向边,Pa和Pb之间存在一条有向边(Pa, Pb)当且仅当Pa被Pb覆盖。
对于每一张海报pi,我们通过搜索它所能达到的所有节点,就可以求出撕下它所能带下的海报有多少..
那么我们就将问题转化成如何去建一个有向图,这就需要我们判断两个矩形之间的关系,我们可以从反面来考虑,两个矩形不相离,
它们就一定相交了..如果相交了,就放入vector中存有向图.
int check(int i,int j)
{
	return !(a1[j]>=a2[i]||a1[i]>=a2[j]||b1[j]>=b2[i]||b1[i]>=b2[j]);
}

还需要我们注意的就是:如果该海报的四个角都被覆盖了的话是不能撕掉它的,那么覆盖它的那些海报也不能被撕掉..
这里我们采用状态压缩记录这个海报四个角的状态,
	if(a1[sta]>a1[x]&&a1[sta]<a2[x]&&b2[sta]>b1[x]&&b2[sta]<b2[x])
	str|=1;
	if(a1[sta]>a1[x]&&a1[sta]<a2[x]&&b1[sta]>b1[x]&&b1[sta]<b2[x])
	str|=2;
	if(a2[sta]>a1[x]&&a2[sta]<a2[x]&&b2[sta]>b1[x]&&b2[sta]<b2[x])
	str|=4;
	if(a2[sta]>a1[x]&&a2[sta]<a2[x]&&b1[sta]>b1[x]&&b1[sta]<b2[x])
	str|=8;

这里采用的是或,不能是+,在我这样的菜鸡的思想里 或和+ 在大部分时间是一样的...还是我ak爷强, ak巨说在 没有进位的情况下或和+是相同的,但是或不进位,即1|1还等于1  而+的话会产生进位;我一开始就是采用+来判断的状态但是错了...不如4+4+4+2+1 就是15
而用状态压缩  | 的话  只有当四个状态都有的时候才会是15  


剩余的就是dfs了


还有就是 竟然不能定义全局的数组y1?这是为什么?哪位大神可以告诉我一下吗?
#include <iostream>
#include <cstdio>  
#include <cstring>  
#include <algorithm>  
#include <vector>  
using namespace std;  
int const MAX = 1e3 + 5;  
int w, h, n;  
int a1[MAX], b1[MAX], a2[MAX], b2[MAX];  
int book[MAX];  
vector<int> vt[MAX]; 
int sta,str;
int check(int i,int j)
{
	return !(a1[j]>=a2[i]||a1[i]>=a2[j]||b1[j]>=b2[i]||b1[i]>=b2[j]);
}
int cal(int x)
{
	if(a1[sta]>a1[x]&&a1[sta]<a2[x]&&b2[sta]>b1[x]&&b2[sta]<b2[x])
	str|=1;
	if(a1[sta]>a1[x]&&a1[sta]<a2[x]&&b1[sta]>b1[x]&&b1[sta]<b2[x])
	str|=2;
	if(a2[sta]>a1[x]&&a2[sta]<a2[x]&&b2[sta]>b1[x]&&b2[sta]<b2[x])
	str|=4;
	if(a2[sta]>a1[x]&&a2[sta]<a2[x]&&b1[sta]>b1[x]&&b1[sta]<b2[x])
	str|=8;
}
void dfs(int x)
{
	book[x]=1;
	for(int i=0;i<vt[x].size();i++)
	{
		int v=vt[x][i];
		if(!book[v])
		{
			cal(v);
			dfs(v);
		}
	}
	return ;
}
int main()
{
	cin>>w>>h>>n;
	for(int i=1;i<=n;i++)
	{
		scanf("%d %d %d %d",&a1[i],&b1[i],&a2[i],&b2[i]);
	}
	for(int i=1;i<=n;i++)
	{
		for(int j=i+1;j<=n;j++)
		{
			if(check(i,j))
			vt[i].push_back(j);
		}
	}
	int ans=0,id=0,cou=0;
	for(int i=1;i<=n;i++)
	{
		cou=0;
		memset(book,0,sizeof(book));
		sta=i;
		str=0;
		dfs(i);
		if(str==15)
		continue;
		for(int j=1;j<=n;j++)
		{
			cou+=book[j];
		}
		if(ans<cou)
		{
			ans=cou;
			id=i;
		}
	}
	printf("%d %d\n",ans,id);
 	return 0;
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Marcus-Bao

万水千山总是情,只给五角行不行

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值