poj_3168 平面扫描

本文介绍一种算法,通过扫描线技术判断平面上多个矩形间的重叠情况,并统计非重叠矩形的数量。算法首先将每个矩形分解为四个顶点,再对所有顶点按坐标排序,最后沿x轴和y轴扫描,判断矩形间是否重叠。
题目大意

    给定平面上N个矩形的位置(给出矩形的左下角和右上角的坐标),这些矩形有些会有重叠,且重叠只会出现矩形的边重合全部或部分,矩形的顶点重合,而不会出现一个矩形的顶点位于另一个矩形的内部。 
    求出所有不重叠的矩形的个数。

题目分析

    将每个矩形分成4个点,记录每个点所属的矩形id,对4*N个点进行按照x坐标和y坐标排序。 
    然后,沿着x方向进行扫描,对于相同的x,沿着该x坐标,从下向上找那些x坐标等于x的点的y坐标,判断该点所在矩形是否出现重合。若重合,则将该点所属的矩形记为重叠。 
    然后,沿着y方向进行扫描,对于相同的y,沿着该y坐标,从左到右找那些y坐标等于y的点的x坐标,判断该点所在矩形是否出现重合。若重合,则记其所在矩形为重叠。 
    怎么判断沿着某一x坐标或y坐标的那些点所在矩形是否重合呢?以沿着同一x坐标从下到上的那些点为例,用一个变量share_count表示当前时刻进入了几个矩形,从下到上遍历,若点A为某个矩形的下端点,则share_count加1,表示进入一个矩形,否则若A为某个矩形的上端点,share_count减1,表示从一个矩形离开。根据share_count >= 2,可知有矩形重叠,其他情况类似分析,画图可以很容易看出。

    最后,遍历一遍所有的矩形,记下没有重叠的矩形个数即可。

实现(c++)
#include<stdio.h>
#include<vector>
#include<string.h>
#include<algorithm>
using namespace std;
#define MAX_N 200000

struct Point{
	int pos_x;	//x坐标
	int pos_y;	//y坐标
	int rect_id;	//矩形的id
	bool start_x;	//x方向是否是扫描时的入点
	bool start_y;	//方向是否是扫描时的入点
	Point(int p1=0, int p2=0, int id=0, bool sx=0, bool sy = 0) :
		pos_x(p1), pos_y(p2), rect_id(id), start_x(sx), start_y(sy){};
};
Point gPoint[4*MAX_N];

//按照x坐标排序比较函数,用于将 gSortOrder 排序,排序后的gSortOrder 为按照x从小到大排序的 gPoint的索引
bool Cmp_x(int p1, int p2){
	if (gPoint[p1].pos_x == gPoint[p2].pos_x)
		return gPoint[p1].pos_y < gPoint[p2].pos_y;
	return gPoint[p1].pos_x < gPoint[p2].pos_x;
}
//类似 Cmp_x
bool Cmp_y(int p1, int p2){
	if (gPoint[p1].pos_y == gPoint[p2].pos_y)
		return gPoint[p1].pos_x < gPoint[p2].pos_x;
	return gPoint[p1].pos_y < gPoint[p2].pos_y;
}
//排序后的gPoint索引。 按照x或者y坐标将 gPoint 排序后,gPoint中的点的位置。
int gSortOrder[4*MAX_N];
//判断矩形是否有重叠
bool gOverlap[MAX_N];
int main(){
	int n, a, b, c, d;
	while (scanf("%d", &n) != EOF){
		
		for (int i = 0; i < n; i++){
			scanf("%d %d %d %d", &a, &b, &c, &d);
			gPoint[4 * i] = Point(a, b, i, true, true);
			gPoint[4 * i + 1] = Point(a, d, i, true, false);
			gPoint[4 * i + 2] = Point(c, b, i, false, true);
			gPoint[4 * i + 3] = Point(c, d, i, false, false);
		}
		//n 变为点的数目
		n *= 4;
		for (int i = 0; i < n; i++){
			gSortOrder[i] = i;  //排序索引
		}

		memset(gOverlap, false, sizeof(gOverlap));

		//按照x坐标排序
		sort(gSortOrder, gSortOrder + n, Cmp_x);

		int i = 0;
		while(i < n){
			int j = i + 1, k = i;
			int share_count = 1;
			while (j < n && gPoint[gSortOrder[j]].pos_x == gPoint[gSortOrder[i]].pos_x){
				if (gPoint[gSortOrder[k]].pos_y == gPoint[gSortOrder[j]].pos_y){ //两条线重合的情形
					gOverlap[gPoint[gSortOrder[j]].rect_id] = true; 
					gOverlap[gPoint[gSortOrder[k]].rect_id] = true;
				}
				if (gPoint[gSortOrder[j]].start_y){
					share_count++;
					if (share_count >= 2){	//说明有重合
						gOverlap[gPoint[gSortOrder[j]].rect_id] = true;
						gOverlap[gPoint[gSortOrder[j-1]].rect_id] = true;
					}
				}
				else{
					share_count--;
				}
				
				k = j++;
			}
			i = j;
		}
		//按照y坐标排序
		sort(gSortOrder, gSortOrder + n, Cmp_y);
		i = 0;
		while (i < n){
			int j = i + 1, k = i;
			int share_count = 1;
			while (j < n && gPoint[gSortOrder[j]].pos_y == gPoint[gSortOrder[i]].pos_y){
				if (gPoint[gSortOrder[k]].pos_x == gPoint[gSortOrder[j]].pos_x){
					gOverlap[gPoint[gSortOrder[j]].rect_id] = true;
					gOverlap[gPoint[gSortOrder[k]].rect_id] = true;
				}
				if (gPoint[gSortOrder[j]].start_x){
					share_count++;
					if (share_count >= 2){
						gOverlap[gPoint[gSortOrder[j]].rect_id] = true;
						gOverlap[gPoint[gSortOrder[j-1]].rect_id] = true;
					}
				}
				else{
					share_count--;
				}

				k = j++;
			}
			i = j;
		}
		int count = 0;

		//注意,这里的 n 变为 矩形的数目
		for (int i = 0; i < n/4; i++){
			if (!gOverlap[i])
				count++;
		}
		printf("%d\n", count);
	}
	return 0;
}

 

乐播投屏是一款简单好用、功能强大的专业投屏软件,支持手机投屏电视、手机投电脑、电脑投电视等多种投屏方式。 多端兼容与跨网投屏:支持手机、平板、电脑等多种设备之间的自由组合投屏,且无需连接 WiFi,通过跨屏技术打破网络限制,扫一扫即可投屏。 广泛的应用支持:支持 10000+APP 投屏,包括综合视频、网盘与浏览器、美韩剧、斗鱼、虎牙等直播平台,还能将央视、湖南卫视等各大卫视的直播内容一键投屏。 高清流畅投屏体验:腾讯独家智能音画调校技术,支持 4K 高清画质、240Hz 超高帧率,低延迟不卡顿,能为用户提供更高清、流畅的视觉享受。 会议办公功能强大:拥有全球唯一的 “超级投屏空间”,扫码即投,无需安装。支持多人共享投屏、远程协作批注,PPT、Excel、视频等文件都能流畅展示,还具备企业级安全加密,保障会议资料不泄露。 多人互动功能:支持多人投屏,邀请好友加入投屏互动,远程也可加入。同时具备一屏多显、语音互动功能,支持多人连麦,实时语音交流。 文件支持全面:支持 PPT、PDF、Word、Excel 等办公文件,以及视频、图片等多种类型文件的投屏,还支持网盘直投,无需下载和转格式。 特色功能丰富:投屏时可同步录制投屏画面,部分版本还支持通过触控屏或电视端外接鼠标反控电脑,以及在投屏过程中用画笔实时标注等功能。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值