题目
示例
思路
什么是扫描线?
扫描线就是用来解决矩形覆盖面积问题。
假如我们想要计算下图的覆盖面积,我们需要计算两个矩形的面积相加,然后减去中间蓝色交叉的部分。

若有n个矩形,我们需要计算他们两两交叉的面积,然后减去,时间复杂度非常高。
假如我们可以对其进行分割,如图:
我们只需要计算矩形①②③的面积即可,于是问题转化为如何计算矩形①②③的面积。
矩形的宽很好计算,我们只需要把x轴的点按照从小到大的顺序排列即可。
难的是计算矩形的高。于是便有了扫描线。
定义:我们从左向右看,定义矩形的左侧边为入边,右侧边为出边,也就是我们扫描的方向,看起来就像用一个线从左向右来扫描所有的矩形
于是我们从左往右扫,遇到入边的线,则对入边区间进行+1操作,遇到出边的线,那么对出边区间进行-1操作。然后我们遍历整个区间,就得到了矩形的高。
我们举例计算:
当我们扫描到第一条边,是入边,范围是[1,3],于是我们给[1,3] + 1,此时整个范围只有[1,3]被覆盖,高为2,第一个矩形面积为 1 * 2 = 2

当我们扫描到第二条边,是入边,范围是[2,4],于是我们给[2,4] + 1,此时整个范围只有[1,4]被覆盖,高为3,第一个矩形面积为 1 * 3 = 3

当我们扫描到第三条边,是出边,范围是[1,3],于是我们给[1,3] - 1,此时整个范围只有[2,4]被覆盖,高为2,第一个矩形面积为 3 * 2 = 6

当我们扫描到第四条边,是出边,范围是[2,4],于是我们给[2,4] - 1,此时整个范围没有覆盖,结束计算

于是覆盖面积为2 + 3 + 6 = 11
计算高的方式就是区间问题,找所有区间内覆盖的长度,我们可以用线段树解决,线段树详解请移步。
为了方便,我们把所有的y坐标进行离散化处理
代码
typedef struct {
int key;
UT_hash_handle hh;
} HashItem;
typedef struct Tuple {
int val[3];
} Tuple;
HashItem *hashFindItem(HashItem **obj, int key) {
HashItem *pEntry = NULL;
HASH_FIND_INT(*obj, &key, pEntry);
return pEntry;
}
bool hashAddItem(HashItem **obj, int key) {
if (hashFindItem(obj, key)) {
return false;
}
HashItem *pEntry = (HashItem *)malloc(sizeof(HashItem));
pEntry->key = key;
HASH_ADD_INT(*obj, key, pEntry);
return true;
}
void hashFree(HashItem **obj) {
HashItem *curr = NULL, *tmp = NULL;
HASH_ITER(hh, *obj, curr, tmp) {
HASH_DEL(*obj, curr);
free(curr);
}
}
static inline int cmp1(const void *pa, const void *pb) {
return *(int *)pa - *(int *)pb;
}
static inline int cmp2(const void *pa, const void *pb) {
Tuple *a = (Tuple *)pa, *b = (Tuple *)pb;
if (a->val[0] != b->val[0]) {
return a->val[0] - b->val[0];
} else if (a->val[1] != b->val[1]) {
return a->val[1] - b->val[1];
} else {
return a->val[2] - b->val[2];
}
}
int rectangleArea(int** rectangles, int rectanglesSize, int* rectanglesColSize){
int n = rectanglesSize;
HashItem *set = NULL;
for (int i = 0; i < n; i++) {
// 下边界
hashAddItem(&set, rectangles[i][1]);
// 上边界
hashAddItem(&set, rectangles[i][3]);
}
int hboundSize = HASH_COUNT(set);
int hbound[hboundSize], pos = 0;
for (HashItem *pEntry = set; pEntry != NULL; pEntry = pEntry->hh.next) {
hbound[pos++] = pEntry->key;
}
hashFree(&set);
qsort(hbound, hboundSize, sizeof(int), cmp1);
// 「思路与算法部分」的 length 数组并不需要显式地存储下来
// length[i] 可以通过 hbound[i+1] - hbound[i] 得到
int seg[hboundSize - 1];
int sweepSize = n * 2;
Tuple sweep[sweepSize];
pos = 0;
memset(seg, 0, sizeof(seg));
for (int i = 0; i < n; ++i) {
// 左边界
sweep[pos].val[0] = rectangles[i][0];
sweep[pos].val[1] = i;
sweep[pos].val[2] = 1;
pos++;
// 右边界
sweep[pos].val[0] = rectangles[i][2];
sweep[pos].val[1] = i;
sweep[pos].val[2] = -1;
pos++;
}
qsort(sweep, sweepSize, sizeof(Tuple), cmp2);
long long ans = 0;
for (int i = 0; i < n * 2; ++i) {
int j = i;
while (j + 1 < sweepSize && sweep[i].val[0] == sweep[j + 1].val[0]) {
++j;
}
if (j + 1 == sweepSize) {
break;
}
// 一次性地处理掉一批横坐标相同的左右边界
for (int k = i; k <= j; ++k) {
int idx = sweep[k].val[1], diff = sweep[k].val[2];
int left = rectangles[idx][1], right = rectangles[idx][3];
for (int x = 0; x < hboundSize - 1; ++x) {
if (left <= hbound[x] && hbound[x + 1] <= right) {
seg[x] += diff;
}
}
}
int cover = 0;
for (int k = 0; k < hboundSize - 1; ++k) {
if (seg[k] > 0) {
cover += (hbound[k + 1] - hbound[k]);
}
}
ans += (long long)cover * (sweep[j + 1].val[0] - sweep[j].val[0]);
i = j;
}
return ans % (long long)(1e9 + 7);
}
257

被折叠的 条评论
为什么被折叠?



