POJ 1151 Atlantis 扫描线+离散化+线段树

/**
 * @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;
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值