POJ 1177 - Picture | 漂浮法

题面:传送门
题意:有N个矩形,按顺序向上覆盖,给你每个矩形左下角顶点的坐标与右上角顶点的坐标。求矩形覆盖后的周长(具体定义见下图)。
在这里插入图片描述
在这里插入图片描述
这道题是我在看2528漂浮法题解的时候看到的,博主整理了一下漂浮法的题目,其中就包括这道1177。既然都说了是漂浮法,那就直接上啊(开始的时候是这么想的,但是很快就发现事情并没有那么简单)。

最开始,我想对整体矩形进行操作,即根据两个顶点判断他们的交并关系。但是仔细分下来,无交集有4种情况,全覆盖(包含)有1种情况,部分相交有15种情况。这15种情况,按照漂浮法“不相交部分裂开继续向上漂”的思想,又要分成裂成2块、3块、4块、5块分别讨论。这个分类讨论实现起来非常棘手。终于写好之后,又发现这种方法漏下了仅有一边相交的情况——写到这里就该反应过来,不可以继续了。因为即使修复了这个Bug,很大可能还有漏掉的情况。这样下去是没有尽头的ORZ而且漂浮法的优势本就在于它大大简化了线段树繁琐的代码,写的时候我要吐了(还不如线段树有模板舒服)。

这个时候就又需要强大的队友的大脑 来拯救我了。队友听我讲了2528用线段树的思路之后,发现了漂浮法的本质:把矩形分成四根线分别向上漂!于是又回到了2528最基本的思路~我们在无交集、全覆盖之后的相交情况,针对横线/竖线只需要讨论三种情况:盖住左边,盖住右边,盖住中间(这种情况包含了边重合)。

好不容易写掉之后,终于如愿以偿地AC了 TLE了!吐了。这道题最难受的地方就是,全网都搜不到漂浮法的题解。只有一位聚聚博客里提到这题可以用漂浮法。我觉得我们的基本思路——拆成线向上漂——应该是没问题的了,聚聚能AC估计就是有哪些地方巧妙地优化了。不过想破头也想不出了……

短暂的三天漂浮法学习之后,坚定了我学好线段树的意念(。

总结一下这道题出现的问题:

  • 分类讨论不要漏掉特殊情况,比如这种覆盖,想想边重合,多个全覆盖嵌套的情况
  • 样例要仔细推,矩形套矩形的情况,内部矩形的周长是不算的(而如果直接漂浮,这个小矩形之上恰好没有覆盖它的矩形的话,我们就会算多一部分),最后debug的时候才发现这个问题。其实读题认真推下样例就可以避免这一点。
#define _CRT_SECURE_NO_WARNINGS

#include <cstdio>
#include <algorithm>
#include <iostream>

using namespace std;

#define MAX_N 5010 //矩形个数
#define ll long long 

struct rectangle
{
	int x1, x2, y1, y2, mark;//x1y1左下角 x2y2右上角
}rec[MAX_N];

int N;
ll ans;

void linecheck(int x1, int y1, int x2, int y2, int i, int pos)//pos=1竖线 0横线
{
	if (pos)//竖线
	{
		int m = i - 1;
		while (i < N && (rec[i].y1 >= y2 || rec[i].y2 <= y1 || rec[i].x2 < x1 || rec[i].x1 > x1))
		{
			if (rec[i].x2 <= rec[m].x2 && rec[i].y2 <= rec[m].y2 && rec[i].y1 >= rec[m].y1 && rec[i].x1 >= rec[m].x1)
			{
				if (rec[i].mark) continue;
				ans -= (rec[i].x2 - rec[i].x1) * 2 + (rec[i].y2 - rec[i].y1) * 2;
				rec[i].mark = 1;
			}
			i++;
		}

		if (i >= N)
		{
			ans += y2 - y1;
			return;
		}

		if (rec[i].y2 >= y2 && rec[i].y1 <= y1)
		{
			ans -= y2 - y1;
			return;
		}

		if (rec[i].y1 > y1 && rec[i].y2 > y2)
		{
			linecheck(x1, y1, x2, rec[i].y1, i+1, 1);
			ans -= y2 - rec[i].y1;
			return;
		}

		if (rec[i].y2 < y2 && rec[i].y1 < y1)
		{
			linecheck(x1, rec[i].y2, x2, y2, i+1, 1);
			ans -= rec[i].y2 - y1;
			return;
		}

		if (rec[i].y2 < y2 && rec[i].y1 > y1)
		{
			linecheck(x1, rec[i].y2, x2, y2, i + 1, 1);
			linecheck(x1, y1, x2, rec[i].y1, i + 1, 1);
			ans -= rec[i].y2 - rec[i].y1;
			return;
		}
	}


	else//横线
	{
		while (i < N && (rec[i].x2 <= x1 || rec[i].x1 >= x2 || rec[i].y1 > y1 || rec[i].y2 < y1) )
			i++;

		if (i >= N)
		{
			ans += x2 - x1;
			return;
		}

		if (rec[i].x1 <= x1 && rec[i].x2 >= x2)
		{
			ans -= x2 - x1;
			return;
		}

		if (rec[i].x2 < x2 && rec[i].x1 < x1)
		{
			linecheck(rec[i].x2, y1, x2, y2, i + 1, 0);
			ans -= rec[i].x2 - x1;
			return;
		}

		if (rec[i].x1 > x1 && rec[i].x2 > x2)
		{
			linecheck(x1, y1, rec[i].x1, y2, i + 1, 0);
			ans -= x2 - rec[i].x1;
			return;
		}

		if (rec[i].x1 > x1 && rec[i].x2 < x2)
		{
			linecheck(x1, y1, rec[i].x1, y2, i + 1, 0);
			linecheck(rec[i].x2, y1, x2, y2, i + 1, 0);
			ans -= rec[i].x2 - rec[i].x1;

			return;
		}

	}
}


int main()
{
	scanf("%d", &N);
	for (int i = 0; i < N; i++)
	{
		scanf("%d %d %d %d", &rec[i].x1, &rec[i].y1, &rec[i].x2, &rec[i].y2);//读入
		rec[i].mark = 0;
	}

	for (int i = 0; i < N - 1; i++)
	{
		linecheck(rec[i].x1, rec[i].y1, rec[i].x1, rec[i].y2, i + 1, 1);
		linecheck(rec[i].x2, rec[i].y1, rec[i].x2, rec[i].y2, i + 1, 1);
		linecheck(rec[i].x1, rec[i].y1, rec[i].x2, rec[i].y1, i + 1, 0);
		linecheck(rec[i].x1, rec[i].y2, rec[i].x2, rec[i].y2, i + 1, 0);
	}

	ans += (rec[N - 1].x2 - rec[N - 1].x1) * 2 + (rec[N-1].y2 - rec[N-1].y1) * 2;
	printf("%lld\n", ans);

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值