[矩形切割][离散化][usaco3.1.4]Shaping Regions

这篇博客探讨了如何解决USACO问题3.1.4 Shaping Regions,涉及将N个长方形放置在AxB的白纸上,要求描述长方形的放置方式,输入输出格式,以及两种解决方法:矩形切割和离散化+倒序染色。文章提到了线性时间解决方案的挑战,并提供了O(n^2)的解决方案。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Shaping Regions形成的区域

译 by tim green


目录

 [隐藏

[编辑]描述

N个不同的颜色的不透明的长方形(1 <= N <= 1000)被放置在一张横宽为A竖长为B的白纸上。 这些长方形被放置时,保证了它们的边与白纸的边缘平行。 所有的长方形都放置在白纸内,所以我们会看到不同形状的各种颜色。 坐标系统的原点(0,0)设在这张白纸的左下角,而坐标轴则平行于边缘。

[编辑]格式

PROGRAM NAME: rect1

INPUT FORMAT:

(file rect1.in)

按顺序输入放置长方形的方法。第一行输入的是那个放在底的长方形(即白纸)。

第 1 行: A , B 和 N由空格分开 (1 <=A, B<=10,000)

第 2 到N+1行: 为五个整数 llx, lly, urx, ury, color 这是一个长方形的左下角坐标,右上角坐标(x+1,y+1)和颜色。

颜色 1和底部白纸的颜色相同。 (1 <= color <= 2500)

OUTPUT FORMAT

(file rect1.out)

输出且仅输出所有能被看到颜色,和该颜色的总面积(可以由若干个不连通的色块组成),按color增序排列。

[编辑]SAMPLE INPUT

20 20 3
2 2 18 18 2
0 8 19 19 3
8 0 10 19 4

.......

[编辑]SAMPLE OUTPUT

1 91
2 84
3 187
4 38

[编辑]INPUT EXPLANATION

请注意:被(0,0)和(2,2)所描绘的是2个单位宽、2个单位高的区域

这里有一个示意图输入:

11111111111111111111
33333333443333333331
33333333443333333331
33333333443333333331
33333333443333333331
33333333443333333331
33333333443333333331
33333333443333333331
33333333443333333331
33333333443333333331
33333333443333333331
33333333443333333331
11222222442222222211
11222222442222222211
11222222442222222211
11222222442222222211
11222222442222222211
11222222442222222211
11111111441111111111
11111111441111111111

'4'在(8,0)与(10,19)形成的是宽为2的区域,而不是3.(也就是说,4形成的区域包含(8,0)和(8,1) ,而不是(8,0)和(8,2)) 。


方法一:矩形切割


提交了两次,因为面积为零的表示不存在,不能输出。

两天尝试了自己研究矩形切割的算法而失败。


我的思路建立在线性时间解决问题上,即由上到下依次求并。

我的两种设想:

1、完全分类讨论,两个矩形有17种相对位置关系,每种位置关系切割出的矩形数目不同,显然这种方法很难实现。

2、双进程递归,两个进程交替进行,即横竖交替,显然这样的方法速度会有很大影响。


都不能实现。现在常用的方法则摒弃了线性,退而求其次,O(n^2),事实证明能过所有数据,不知为什么。。

外层循环只考虑一个矩形,被上层的所有矩形切割后的结果,并集的实现则累加切割后的小矩形即可。

这样的话,进行了几次外层循环就得到了几个矩形的并集。


用S(A)表示A的面积。则有

S(A ∩ CuB) = S(A∪B) - S(B)


/*
ID: wuyihao1
LANG: C++
TASK: rect1
*/

#include <cstdio>
#include <algorithm>
using std::sort;

int lx[10010];
int rx[10010];
int ly[10010];
int ry[10010];
int col[10010];
int col2[10010];
int ans[10010];
int n;
int rec;

void rect(int i,int llx,int lly,int rrx,int rry)
{
	while (i<n+1 && (llx>rx[i]||rrx<lx[i]||lly>ry[i]||rry<ly[i])) i ++;
	if (i == n+1) { rec += (rrx-llx)*(rry-lly); return; }
	if (lx[i]>llx){rect(i+1,llx,lly,lx[i],rry);llx=lx[i];}
	if (rx[i]<rrx){rect(i+1,rx[i],lly,rrx,rry);rrx=rx[i];}
	if (ly[i]>lly){rect(i+1,llx,lly,rrx,ly[i]);}
	if (ry[i]<rry){rect(i+1,llx,ry[i],rrx,rry);}
}

int main()
{
	freopen("rect1.in","r",stdin);
	freopen("rect1.out","w",stdout);

	scanf("%d%d%d",rx+1,ry+1,&n);
	lx[1] = ly[1] = 0;
	col2[1] = col[1] = 1;
	n ++;

	for (int i=2;i<n+1;i++)
	{
		scanf("%d%d%d%d%d",lx+i,ly+i,rx+i,ry+i,col+i);
		col2[i] = col[i];
	}

	ans[col[n]] = rec = (ry[n]-ly[n])*(rx[n]-lx[n]);
	for (int i=n-1;i>0;i--)
	{
		int last = rec;
		rect(i+1,lx[i],ly[i],rx[i],ry[i]);
		ans[col[i]] += rec-last;
	}
	
	sort(col2+1,col2+n+1);

	col2[0] = -0x3f3f3f3f;
	for (int i=1;i<n+1;i++)
	{
		if (col2[i] != col2[i-1] && ans[col2[i]]>0)
		{
			printf("%d %d\n",col2[i],ans[col2[i]]);
		}
	}

	return 0;
}


方法二:离散化+倒序染色


交了两次才过。因为离散化后,横边和纵边都为2N,而不是N。

注意跳过上下或左右相等的所有情况,这样的矩形不存在。这样对速度略有优化。

/*
ID: wuyihao1
LANG: C++
TASK: rect1
*/
#include <cstdio>
#include <algorithm>
using std::sort;

int a;
int b;
int n;

int hr[200010];
int vt[200010];
int lx[1010];
int ly[1010];
int rx[1010];
int ry[1010];
int col[1010];
int ans[2510];

int main()
{
	freopen("rect1.in","r",stdin);
	freopen("rect1.out","w",stdout);

	scanf("%d%d%d",&a,&b,&n);

	for (int i=0;i<n;i++)
	{
		scanf("%d%d%d%d%d",&hr[i<<1],&vt[i<<1],&hr[(i<<1)+1],&vt[(i<<1)+1],&col[i+1]);
		lx[i+1] = hr[i<<1];
		ly[i+1] = vt[i<<1];
		rx[i+1] = hr[(i<<1)+1];
		ry[i+1] = vt[(i<<1)+1];
	}

	n ++;
	col[0] = 1;
	lx[0] = 0;
	ly[0] = 0;
	rx[0] = a;
	ry[0] = b;
	hr[(n-1)<<1] = 0;
	vt[(n-1)<<1] = 0;
	hr[((n-1)<<1)+1] = a;
	vt[((n-1)<<1)+1] = b;

	sort(hr,hr+(n<<1));
	sort(vt,vt+(n<<1));

	for (int i=1;i<(n<<1);i++)
	{
		if (hr[i]==hr[i-1])
			continue;
		for (int j=1;j<(n<<1);j++)
		{
			if (vt[j]==vt[j-1])
				continue;
			for (int k=n-1;k>-1;k--)
			{
				if (hr[i-1]>=lx[k] && hr[i]<=rx[k]&&
					vt[j-1]>=ly[k] && vt[j]<=ry[k])
				{
					ans[col[k]] += (hr[i]-hr[i-1])*(vt[j]-vt[j-1]);
					break;
				}
			}
		}
	}

	for (int i=1;i<2501;i++)
	{
		if (ans[i])
			printf("%d %d\n",i,ans[i]);
	}

	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值