POJ 1151 Atlantis

题目大意:

        希腊有几张亚特兰蒂斯的手绘地图,但是每张地图只记录了亚特兰蒂斯的部分区域(具体说每张地图只记录了一片长方形区域),现要求出所有这些区域面积的总和(公共部分只算一次)。

        现有多个测例,每个测例中给出地图数量n(1 ≤ n ≤ 100),输入以n = 0结束,接下来会给出每张地图中描述的长方形的左上角坐标和右下角坐标(x轴朝右,y轴朝下,并且所有坐标用浮点数(double)表示,范围为[0, 100000]),对于每个测例求总面积大小。

题目链接

注释代码:

/*         
 * Problem ID : POJ 1151 Atlantis
 * Author     : Lirx.t.Una         
 * Language   : C++        
 * Run Time   : 0 ms         
 * Run Memory : 172 KB         
*/ 

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

//每条扫描线(即矩形的左右两边)
//是矩形的左边还是右边
//用于计数
//比如y区间中[3, 5]刚好有一条矩形的左边
//则此区域内的的有效值就+L(即+1),表示存在一条左边
//如果扫描到该矩形的右边,则此区域就+R(即-1),表示
//在该区域中抵消掉了一条边
#define	L		1
#define	R		-1

//maximum nubmer of scanning lines
//扫描线的最大数量,为矩形数量的两倍
//每个矩形都有左右两条边
#define	MAXSCN		201
//对所有矩形的上下边的y坐标离散化、唯一化后的
//y值的个数,最多为矩形数量的两倍
//每个矩形都有上下两条边
#define	MAXYN		201
//maximum size of segment tree
//线段树的最大结点数(测试过的最优值)
#define	MAXSGSIZE	530

//求t的左子树
#define	LFT(t)		( t << 1 )
//求t的右子树
#define	RHT(t)		( LFT(t) | 1 )
//都采用位操作能提高速度

using namespace std;

struct	Scan {//扫描线,是纵向的

	double	x;//扫描线的x坐标
	double	l, h;//该扫描线的y的区间[low, hight]
	char	vv;//virtual value,该扫描线的有效值,可以为L或R

	Scan(void) {}

	Scan( double xx, double ll, double hh, char vv_ ) :
		x(xx), l(ll), h(hh), vv(vv_) {}

	bool//对扫描线按照x递增排序,扫描时就按照这个顺序扫描
	operator<(const Scan &oth)
	const {
	
		return x < oth.x;
	}
};

//将离散化后的y映射到线段树上
//映射的值为y数组的下标
struct	Node {//线段树结点

	short	lft, rht;//lft、rht为离散y的下标映射
	//改线段的有效值,即线段的厚度
	//比如改线段中有n个矩形的左边重合,则vv为n,表示厚度
	short	vv;
	double	len;//改线段的有效长度,被多少长有效的边所覆盖
};

Scan	scn[MAXSCN];//扫描线
Node	seg[MAXSGSIZE];//线段树
double	y[MAXYN];//对矩形y坐标离散外、唯一化(可以减小线段树的规模)

void
build( int tree, int lft, int rht ) {//构造先单数

	seg[tree].lft = lft;
	seg[tree].rht = rht;
	seg[tree].vv  = 0;
	seg[tree].len = 0.0;

	if ( 1 == rht - lft )//这里构建一个叶子结点长度至少为1,不能为0
		return ;

	int		mid;

	mid = ( lft + rht ) >> 1;

	build( LFT(tree), lft, mid );
	build( RHT(tree), mid, rht );
}

void
len_update(int tree) {//跟新当前结点的有效长度

	int		lft, rht;

	lft = seg[tree].lft;
	rht = seg[tree].rht;

	if ( seg[tree].vv )//有效长度大于0直接取映射的实际长度
		seg[tree].len = y[rht] - y[lft];
	//为0但不代表其子树的有效长度为0,因为父结点的有效长度会传递给其子结点
	else if ( 1 == rht - lft )//如果已经到了叶子结点就不用再向下探索了
		seg[tree].len = 0;
	else//否则就归纳(自底向上)求当前结点的有效长度
		seg[tree].len = seg[ LFT(tree) ].len + seg[ RHT(tree) ].len;
}

void
update( int tree, Scan &s ) {//对线段树进行跟新
	//最主要是利用扫描线跟新线段树的有效长度以及有效值

	int		lft, rht;

	lft = seg[tree].lft;
	rht = seg[tree].rht;

	if ( y[lft] == s.l && y[rht] == s.h ) {//区间刚好对位
	
		seg[tree].vv += s.vv;//对有效值进行累加
		len_update(tree);//由于线段厚度改变因此需要重新计算其有效长度

		return ;
	}

	int		mid;

	//以下为线段树的常规更新方法

	mid = ( lft + rht ) >> 1;

	if ( s.h <= y[mid] )
		update( LFT(tree), s );
	else if ( s.l >= y[mid] )
		update( RHT(tree), s );
	else {
	
		//中间出现断点
		//用y[mid]接上
		Scan	ss(s);
		ss.h = y[mid];
		update( LFT(tree), ss );

		ss   = s;
		ss.l = y[mid];
		update( RHT(tree), ss );
	}
	len_update(tree);//由于其子树更新过了,因此会影响到整个线段树的有效长度
	//因此需要重新计算其有效长度
}

int
main() {

	int		t;//测例数
	int		n;//矩形数量

	int		ns;//number of scanning lines,扫描线的数量
	int		ny;//number of discrete y,离散化y的个数

	int		i;//计数变量

	double	ans;//最后覆盖的总面积

	//用于接收输入的坐标
	double	x1, y1;
	double	x2, y2;

	t = 0;
	while ( scanf("%d", &n), n ) {
	

		ns = 0;
		ny = 0;

		while ( n-- ) {

			scanf("%lf%lf%lf%lf", &x1, &y1, &x2, &y2);
			scn[ns++] = Scan( x1, y1, y2, L );
			scn[ns++] = Scan( x2, y1, y2, R );
			y[ny++]   = y1;
			y[ny++]   = y2;
		}
		sort(scn, scn + ns);//扫描线按x递增排序
		sort(y, y + ny);//y按照y递增排序,即离散化
		ny = unique(y, y + ny) - y;//STL泛型函数,将容器里的元素唯一化
		//前提是容器必须现排序,返回唯一化后新数组的end迭代器,因此长度缩短
		//需要通过 - y得到新容器的大小

		build( 1, 0, ny - 1 );

		for ( ans = 0.0, i = 0; i < ns; i++ ) {
		
			update( 1, scn[i] );//对整个y区间跟新,得到一个跟新后的有效长度
			ans += seg[1].len * ( scn[i + 1].x - scn[i].x );//逐个扫描并累加面积
			//长有有效值决定,宽为最近两个x相减而得,一直往后递推下去
		}

		printf("Test case #%d\n", ++t);
		printf("Total explored area: %.2lf\n\n", ans);
	}


	return 0;
}

无注释代码:

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

#define	L		1
#define	R		-1

#define	MAXSCN		201
#define	MAXYN		201
#define	MAXSGSIZE	530

#define	LFT(t)		( t << 1 )
#define	RHT(t)		( LFT(t) | 1 )

using namespace std;

struct	Scan {

	double	x;
	double	l, h;
	char	vv;

	Scan(void) {}

	Scan( double xx, double ll, double hh, char vv_ ) :
		x(xx), l(ll), h(hh), vv(vv_) {}

	bool
	operator<(const Scan &oth)
	const {
	
		return x < oth.x;
	}
};

struct	Node {

	short	lft, rht;
	short	vv;
	double	len;
};

Scan	scn[MAXSCN];
Node	seg[MAXSGSIZE];
double	y[MAXYN];

void
build( int tree, int lft, int rht ) {

	seg[tree].lft = lft;
	seg[tree].rht = rht;
	seg[tree].vv  = 0;
	seg[tree].len = 0.0;

	if ( 1 == rht - lft )
		return ;

	int		mid;

	mid = ( lft + rht ) >> 1;

	build( LFT(tree), lft, mid );
	build( RHT(tree), mid, rht );
}

void
len_update(int tree) {

	int		lft, rht;

	lft = seg[tree].lft;
	rht = seg[tree].rht;

	if ( seg[tree].vv )
		seg[tree].len = y[rht] - y[lft];
	else if ( 1 == rht - lft )
		seg[tree].len = 0;
	else
		seg[tree].len = seg[ LFT(tree) ].len + seg[ RHT(tree) ].len;
}

void
update( int tree, Scan &s ) {

	int		lft, rht;

	lft = seg[tree].lft;
	rht = seg[tree].rht;

	if ( y[lft] == s.l && y[rht] == s.h ) {
	
		seg[tree].vv += s.vv;
		len_update(tree);

		return ;
	}

	int		mid;

	mid = ( lft + rht ) >> 1;

	if ( s.h <= y[mid] )
		update( LFT(tree), s );
	else if ( s.l >= y[mid] )
		update( RHT(tree), s );
	else {
	
		Scan	ss(s);
		ss.h = y[mid];
		update( LFT(tree), ss );

		ss   = s;
		ss.l = y[mid];
		update( RHT(tree), ss );
	}
	len_update(tree);
}

int
main() {

	int		t;
	int		n;

	int		ns;
	int		ny;

	int		i;

	double	ans;

	double	x1, y1;
	double	x2, y2;

	t = 0;
	while ( scanf("%d", &n), n ) {
	

		ns = 0;
		ny = 0;

		while ( n-- ) {

			scanf("%lf%lf%lf%lf", &x1, &y1, &x2, &y2);
			scn[ns++] = Scan( x1, y1, y2, L );
			scn[ns++] = Scan( x2, y1, y2, R );
			y[ny++]   = y1;
			y[ny++]   = y2;
		}
		sort(scn, scn + ns);
		sort(y, y + ny);
		ny = unique(y, y + ny) - y;

		build( 1, 0, ny - 1 );

		for ( ans = 0.0, i = 0; i < ns; i++ ) {
		
			update( 1, scn[i] );
			ans += seg[1].len * ( scn[i + 1].x - scn[i].x );
		}

		printf("Test case #%d\n", ++t);
		printf("Total explored area: %.2lf\n\n", ans);
	}


	return 0;
}

单词解释:

Greek:n, 希腊人,希伯来语; adj, 希腊人的

fable:n, 寓言

fabled:adj, 寓言中的

Atlantis:地名,亚特兰蒂斯(传说中沉没于大西洋的岛屿)

unwise:adj, 不明智的

top-left:左上角

bottom-right:右下角

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值