• poj_1151 线段树


    题目大意

        在平面上给定n个矩形,可以相互覆盖全部或者部分,求出矩形占据的总面积。

    题目分析

        将矩形按照x方向的进行分割之后,将平面沿着y方向划分一系列单元(不定高度),每个矩形在y方向上占据若干连续的单元;在x方向上,将矩形按照x坐标排序之后,考虑有一个扫描线从左到右扫描,当扫描线进入矩形之后,所有矩形在扫描线上占据的总长度有可能增加,而扫面线离开某个矩形时,所有矩形在扫描线上占据的总长度有可能减少。 
        在计算面积的时候,将当前扫描点 所有矩形在扫描线上占据的总长度 乘以 当前扫描点到下一扫描点的长度,直到所有矩形均出扫描线。区间操作,考虑使用线段树

    实现(c++)

    #define _CRT_SECURE_NO_WARNINGS
    #include<stdio.h>
    #include<algorithm>
    #include<string.h>
    #include<vector>
    using namespace std;
    #define MAX_RECT_NUM 1000
    #define MAX_SEG_NUM MAX_RECT_NUM * 2
    #define MAX_NODE_NUM 4*MAX_SEG_NUM
    #define MAX(a, b) a > b? a :b
    #define MIN(a, b) a < b? a :b
    //根据矩形的上下边在y方向上划分区间单元(长度不固定),每个矩形占据y方向上的连续的几个单元,形成区间
    struct Rect{
    	double top_left_x;
    	double top_left_y;
    	double bottom_right_x;
    	double bottom_right_y;
    
    	int interval_beg;	//在y轴上,该矩形所占据区间的起始单元序号
    	int interval_end;	//在y轴上,该矩形所占据区间的结束单元序号(从下向上)  inteval_beg 和 interval_end为 闭区间
    };
    
    Rect gRects[MAX_RECT_NUM];
    vector<double> gPartPoint;		//用于离散化的点纵坐标
    vector<double> gSegs;			//离散化之后的段单元长度
    
    struct Node{
    	int beg;		//在y轴方向离散化之后,节点所代表区间的起始块号
    	int end;		//节点所代表区间的终止块号
    	double length;	//节点所代表区间的长度(y轴方向)
    	int covered_num;	//扫描线被多少个矩形覆盖
    };
    Node gNodes[MAX_NODE_NUM];
    
    //对矩形进行x坐标从小到大排序,便于进行扫描
    bool CmpToSortRect(const Rect& rect1, const Rect& rect2){
    	if (rect1.top_left_x == rect2.top_left_x)
    		return rect1.bottom_right_x < rect2.bottom_right_x;
    	return rect1.top_left_x < rect2.top_left_x;
    }
    
    void BuildTree(int beg, int end, int index){
    	gNodes[index].beg = beg;
    	gNodes[index].end = end;
    	gNodes[index].covered_num = 0;
    	if (beg == end){
    		gNodes[index].length = gSegs[beg];
    		return;
    	}
    	int left = 2 * index + 1;
    	int right = 2 * index + 2;
    	int mid = (beg + end) / 2;
    	BuildTree(beg, mid, left);
    	BuildTree(mid + 1, end, right);
    	//由子节点长度得到父节点代表区间的长度
    	gNodes[index].length = gNodes[left].length + gNodes[right].length;
    }
    
    //向下更新
    void PushDown(int index){
    	if (gNodes[index].covered_num){
    		int left = 2 * index + 1, right = 2 * index + 2;
    		gNodes[left].covered_num += gNodes[index].covered_num;
    		gNodes[right].covered_num += gNodes[index].covered_num;
    	}
    	gNodes[index].covered_num = 0;
    }
    
    //向上更新
    void PushUp(int index){
    	int left = 2 * index + 1, right = 2 * index + 2;
    	int min = MIN(gNodes[left].covered_num, gNodes[right].covered_num);
    	gNodes[index].covered_num = min;
    	gNodes[left].covered_num -= min;
    	gNodes[right].covered_num -= min;
    }
    
    //当扫描线进入矩形区域时step_in = true, 否则为false
    void Update(int beg, int end, int index, bool step_in){
    	if (gNodes[index].beg >= beg && gNodes[index].end <= end){
    		if (step_in){
    			gNodes[index].covered_num++;
    		}
    			
    		else{
    			gNodes[index].covered_num--;
    		}
    		return;
    	}
    	if (gNodes[index].end < beg || gNodes[index].beg > end){
    		return;
    	}
    	if (beg > end){
    		return;
    	}
    	int left = 2 * index + 1, right = 2 * index + 2;
    	int mid = (gNodes[index].beg + gNodes[index].end) / 2;
    	//向下递归时,先pushdown 向下更新
    	PushDown(index);
    	Update(beg, MIN(mid, end), left, step_in);
    	Update(MAX(mid + 1, beg), end, right, step_in);
    	//递归返回进行 向上更新
    	PushUp(index);
    }
    
    //查询,查询当前情况下,扫描线占据的矩形y方向长度
    double Query(int index){
    	if (gNodes[index].covered_num > 0){
    		return gNodes[index].length;
    	}
    	if (gNodes[index].beg == gNodes[index].end){
    		return 0;
    	}
    	int left = 2 * index + 1, right = 2 * index + 2;
    	return Query(left) + Query(right);
    }
    
    bool DoubleEqual(double d1, double d2){
    	if (abs(d1 - d2) < 1e-7){
    		return true;
    	}
    	return false;
    }
    int main(){
    	int n, cas = 1;
    	while (true){
    		scanf("%d", &n);
    		if (n == 0){
    			break;
    		}
    		gPartPoint.clear();
    		for (int i = 0; i < n; i++){
    			scanf("%lf %lf %lf %lf", &gRects[i].top_left_x, &gRects[i].top_left_y, &gRects[i].bottom_right_x, &gRects[i].bottom_right_y);
    			gPartPoint.push_back(gRects[i].top_left_y);		 //得到y方向上的各个离散的分界点
    			gPartPoint.push_back(gRects[i].bottom_right_y);
    		}
    		//对分界点进行排序,去重
    		sort(gPartPoint.begin(), gPartPoint.end());
    		vector<double>::iterator it = unique(gPartPoint.begin(), gPartPoint.end());
    		gPartPoint.erase(it, gPartPoint.end());
    
    		//根据分界点,得到离散化之后的区间长度
    		gSegs.clear();
    		gSegs.reserve(gPartPoint.size());
    		for (int i = 0; i < gPartPoint.size() - 1; i++){
    			double len = gPartPoint[i + 1] - gPartPoint[i];
    			gSegs.push_back(len);
    		}
    
    		//得到每个矩形在y方向上占据的离散化之后的区间的  beg和end(闭区间)
    		for (int i = 0; i < n; i++){
    			vector<double>::iterator it = find(gPartPoint.begin(), gPartPoint.end(), gRects[i].top_left_y);
    			gRects[i].interval_beg = it - gPartPoint.begin();
    			it = find(gPartPoint.begin(), gPartPoint.end(), gRects[i].bottom_right_y);
    			gRects[i].interval_end = it - gPartPoint.begin() - 1;
    		}
    		
    		BuildTree(0, gSegs.size() - 1, 0);
    
    		//将x方向的各个分割点进行排序,去重
    		gPartPoint.clear();
    		for (int i = 0; i < n; i++){
    			gPartPoint.push_back(gRects[i].top_left_x);
    			gPartPoint.push_back(gRects[i].bottom_right_x);
    		}
    		sort(gPartPoint.begin(), gPartPoint.end());
    		it = unique(gPartPoint.begin(), gPartPoint.end());
    		gPartPoint.erase(it, gPartPoint.end());
    
    
    		int seg_num = gSegs.size();
    		double sum_area = 0;
    		double height = 0;
    		int beg, end;
    		for (int i = 0; i < gPartPoint.size() - 1; i++){
    		
    			for (int r = 0; r < n; r++){
    				if (DoubleEqual(gRects[r].top_left_x, gPartPoint[i])){ //扫描线进入矩形					
    					Update(gRects[r].interval_beg, gRects[r].interval_end, 0, true);
    				}
    				if (DoubleEqual(gRects[r].bottom_right_x, gPartPoint[i])){//扫描线离开矩形
    					Update(gRects[r].interval_beg, gRects[r].interval_end, 0, false);
    				}
    
    			}
    			height = Query(0);
    			sum_area += height*(gPartPoint[i + 1] - gPartPoint[i]);
    		}
    		printf("Test case #%d
    ", cas ++);
    		printf("Total explored area: %.2lf
    
    ", sum_area);
    	}
    	return 0;
    }
    
  • 相关阅读:
    将十进制的颜色制转换成ARGB
    HTTPS从认识到线上实战全记录
    如何从零开始对接第三方登录(Java版):QQ登录和微博登录
    JS弹出下载对话框以及实现常见文件类型的下载
    【干货】Chrome插件(扩展)开发全攻略
    Lucene5.5.4入门以及基于Lucene实现博客搜索功能
    ReactNative与NativeScript对比报告
    JavaScript常见原生DOM操作API总结
    JS获取剪贴板图片之后的格式选择与压缩问题
    详细记录一下网站备案经过,备案真的很简单
  • 原文地址:https://www.cnblogs.com/gtarcoder/p/4784055.html
Copyright © 2020-2023  润新知