/**
* @file main.cpp
* @brief 扫描线+离散化+线段树,题意是要求一堆矩形的总面积,需要处理矩形相交
* ac这道题着实让我为难了很久,第一次写扫描线,第一次接触离散化
*
* 基本思路是这样:
* 1. 构建平行于x轴的扫描线并进行排序
* 这样,任意两条扫描线之间的宽度乘以在高方向上的有效长度就是这一段的面积
* 2. 高方向上的有效长度计算需要使用到线段树以及Y坐标离散化
* 3. 高方向上的有效长度更新(线段树的更新)分两种:添加一个区间,删除一个区间
* 分别对应同一个矩形的左右两条扫描线
* 4. 引入区间的引用计数来处理线段树更新的问题
*
* @author yekeren
* @version 1.0.0
* @date 2013-06-14
*/
#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define LIMIT 400
#define LEFT(x) (((x) << 1) + 1)
#define RIGHT(x) (((x) << 1) + 2)
///<扫描线结构
struct scanline_t {
double x, y1, y2;
unsigned char bleft;
} scanline[LIMIT];
int nscanline = 0;
///<Y轴离散化结构
double axis[LIMIT] = { 0 };
int naxis = 0;
///<线段树结构
struct range_t {
int refercount;
double length;
} range[LIMIT * 4];
/**
* @brief Y轴坐标排序比较函数
* @param p1
* @param p2
* @return
*/
int axis_compare(const void *p1, const void *p2)
{
const double *v1 = (const double*)p1;
const double *v2 = (const double*)p2;
if (*v1 > *v2) {
return 1;
} else if (*v1 < *v2) {
return -1;
} else {
return 0;
}
}
/**
* @brief Y轴坐标值唯一化处理
* @param axis
* @param n
*/
void axis_uniq(double axis[], int &n)
{
if (n == 0) {
return;
}
int m = 0;
for (int i = 1; i < n; ++i)
{
if (axis[i] != axis[m]) {
axis[++m] = axis[i];
}
}
n = 1 + m;
}
/**
* @brief 根据Y坐标查找序号
* @param axis[]
* @param n
* @param v
* @return
*/
int axis_find(double axis[], int n, double v)
{
int left = 0;
int right = n - 1;
while (left <= right)
{
int mid = (left + right) >> 1;
if (v < axis[mid]) {
right = mid - 1;
} else if (v > axis[mid]) {
left = mid + 1;
} else {
return mid;
}
}
return -1;
}
/**
* @brief 扫描线排序比较函数
* @param p1
* @param p2
* @return
*/
int scanline_compare(const void *p1, const void *p2)
{
const scanline_t *v1 = (const scanline_t*)p1;
const scanline_t *v2 = (const scanline_t*)p2;
if (v1->x > v2->x) {
return 1;
} else if (v1 ->x < v2->x) {
return -1;
} else {
return 0;
}
}
/**
* @brief 线段树插入数据
* @param root
* @param left
* @param right
* @param a
* @param b
*/
void insert(int root, int left, int right, int a, int b)
{
if (a == left && b == right)
{
++range[root].refercount;
range[root].length = axis[b] - axis[a - 1];
return;
}
int mid = (left + right) >> 1;
if (b <= mid) {
insert(LEFT(root), left, mid, a, b);
} else if (a > mid) {
insert(RIGHT(root), mid + 1, right, a, b);
} else {
insert(LEFT(root), left, mid, a, mid);
insert(RIGHT(root), mid + 1, right, mid + 1, b);
}
if (range[root].refercount == 0) {
range[root].length = range[LEFT(root)].length + range[RIGHT(root)].length;
}
}
/**
* @brief 线段树删除数据
* @param root
* @param left
* @param right
* @param a
* @param b
*/
void remove(int root, int left, int right, int a, int b)
{
if (a == left && b == right)
{
if (--range[root].refercount == 0)
{
if (left == right) {
range[root].length = 0;
} else {
range[root].length = range[LEFT(root)].length + range[RIGHT(root)].length;
}
}
return;
}
int mid = (left + right) >> 1;
if (b <= mid) {
remove(LEFT(root), left, mid, a, b);
} else if (a > mid) {
remove(RIGHT(root), mid + 1, right, a, b);
} else {
remove(LEFT(root), left, mid, a, mid);
remove(RIGHT(root), mid + 1, right, mid + 1, b);
}
if (range[root].refercount == 0) {
if (left == right) {
range[root].length = 0;
} else {
range[root].length = range[LEFT(root)].length + range[RIGHT(root)].length;
}
}
}
int main(int argc, char *argv[])
{
int n, caseno = 0;
while (std::cin >> n, n != 0)
{
memset(range, 0, sizeof(range));
nscanline = 0;
naxis = 0;
for (int i = 0; i < n; ++i)
{
double x1, y1, x2, y2;
std::cin >> x1 >> y1 >> x2 >> y2;
scanline[nscanline].x = x1;
scanline[nscanline].y1 = y1;
scanline[nscanline].y2 = y2;
scanline[nscanline].bleft = 1;
++nscanline;
scanline[nscanline].x = x2;
scanline[nscanline].y1 = y1;
scanline[nscanline].y2 = y2;
scanline[nscanline].bleft = 0;
++nscanline;
axis[naxis++] = y1;
axis[naxis++] = y2;
}
qsort(axis, naxis, sizeof(axis[0]), axis_compare);
qsort(scanline, nscanline, sizeof(scanline[0]), scanline_compare);
axis_uniq(axis, naxis);
double area = 0;
for (int i = 0; i < nscanline - 1; ++i)
{
int y1 = axis_find(axis, naxis, scanline[i].y1);
int y2 = axis_find(axis, naxis, scanline[i].y2);
if (scanline[i].bleft) {
insert(0, 1, naxis, y1 + 1, y2);
} else {
remove(0, 1, naxis, y1 + 1, y2);
}
area += range[0].length * (scanline[i + 1].x - scanline[i].x);
}
printf("Test case #%d\nTotal explored area: %.2lf\n\n", ++caseno, area);
}
return 0;
}
POJ 1151 Atlantis 扫描线+离散化+线段树
最新推荐文章于 2023-06-02 00:08:09 发布