扫描线 笔记(面积并问题)

P5490 【模板】扫描线

扫描线主要解决的是几个矩形的面积并问题.

当然也可以用来解决周长并问题 , 不过由于暂时不会就不说了.

比如下面的几个矩形:

我们假设有一条垂直于 x x x 轴的直线从左往右扫描, 下图将它标记为红色. 不难想到可以计算这条直线扫过的面积来得到所有矩形的面积并.

显然这条直线会碰到最左边的垂直于 x x x 轴的边.

把从最左边的垂直于 x x x 轴的边开始到第二条垂直于 x x x 轴的边中间扫过的矩形的面积标记下来.

此时扫过的矩形的面积就是第一条垂直于 x x x 轴的边长度乘上从最左边的垂直于 x x x 轴的边开始到第二条垂直于 x x x 轴的边扫过的距离.

以此类推, 这是扫到第 3 3 3 条垂直于 x x x 轴的边.

新扫过的面积即橙色面积就是前两个垂直于 x x x 轴的边的长度和乘新扫过的距离.

而扫到第三条垂直于 x x x 轴的边的时候我们发现前三条垂直于 x x x 轴的边的纵坐标有重复部分. 这是怎么计算呢?

可以把覆盖的区间标记一遍, 最后看总长度.

扫到黄色区间结束的时候, 我们发现有一条边不用再继续往下算了(就是最下面那条), 然后我们把它删去即可.

其实我们可以在插入一条边的时候把这个边里的点对应的纵坐标加一, 删除的时候减一, 然后可以达到这种效果.

然后我们发现, 我们要一个可以支持区间操作的数据结构, 可以用线段树来维护这条扫描线.

附上扫描结束的几张图:

然后看怎么实现:

首先要知道存什么: 我们想知道的是每一个矩形两条垂直于 x x x 轴的边. 还要标记一下这是左边那条还是右边那条.

那其实只要保存一条边的上端点和下端点的纵坐标及横坐标外加一个标记就够了.

注意有 n n n 个矩形, 就要存 2 n 2n 2n 条边.

int n, lcnt;

//...

struct node1{
   
	long long x, highy, lowy, flag;
}l[200005];

node1 make_node1(long long xx, long long hy, long long ly, long long f){
   //构造函数
	node1 T;
	T.x = xx;
	T.highy = hy;
	T.lowy = ly;
	T.flag = f;
	return T;
}

//...

for(int i = 1; i <= n; ++i){
   
	int x_1, x_2, y_1, y_2;
	scanf("%d%d%d%d" ,&x_1 ,&y_1 ,&x_2 ,&y_2);
	l[++lcnt] = make_node1(x_1, y_2, y_1, 1);
	l[++lcnt] = make_node1(x_2, y_2, y_1, -1);
}

别忘了把这些边按 x x x 排序.

但是我们看数据范围:

对于 % 100 \% 100 %100 的数据, 1 ≤ n ≤ 1 0 5 1 \le n \le 10^5 1n105 0 ≤ x 1 ≤ x 2 ≤ 1 0 9 0 \le x_1 \le x_2 \le 10^9 0x1x2109 0 ≤ y 1 ≤ y 2 ≤ 1 0 9 0 \le y_1 \le y_2 \le 10^9 0y1y2109

很明显我们如果区间更新的话, 扫描线就要在 [ 0 , 1 0 9 ] [0, 10^9] [0,10

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
扫描线填充算法是一种用于计算机图形学中的多边形填充方法。它的基本思路是将多边形沿着 y 轴方向分成若干条线段,然后对每条线段进行处理。具体实现过程如下: 1. 扫描线初始化:找出多边形的最高点和最低点,然后依次扫描每一行,从上往下扫描。 2. 建立边表:扫描多边形的每条边,建立边表,包括边的起点、终点、率等信息。 3. 建立活性边表:对于每一个扫描线,建立一个活性边表,表示当前扫描线与多边形相交的边。 4. 填充像素:对于每一个扫描线,从左到右扫描活性边表,确定相邻两条边的交点,将这些点之间的像素填充。 下面是 C 语言实现扫描线填充算法的代码: ``` #include <stdio.h> #include <graphics.h> struct Edge { int y_upper; // 边的上端点 float x_inter; // 边与扫描线的交点 float slope_inverse; // 边的率的倒数 }; void add_edge(struct Edge *edges, int *num_edges, int y_upper, float x_inter, float slope_inverse) { int i = *num_edges; while (i > 0 && y_upper < edges[i - 1].y_upper) { edges[i] = edges[i - 1]; i--; } edges[i].y_upper = y_upper; edges[i].x_inter = x_inter; edges[i].slope_inverse = slope_inverse; (*num_edges)++; } void fill_polygon(int *poly_points, int num_points, int color) { struct Edge edges[num_points], edge; int num_edges = 0; int y, i, j, x1, y1, x2, y2; for (i = 0; i < num_points; i++) { x1 = poly_points[2 * i]; y1 = poly_points[2 * i + 1]; x2 = poly_points[(2 * i + 2) % (2 * num_points)]; y2 = poly_points[(2 * i + 3) % (2 * num_points)]; if (y1 == y2) continue; if (y1 > y2) { int temp = y1; y1 = y2; y2 = temp; temp = x1; x1 = x2; x2 = temp; } edge.y_upper = y2 - 1; edge.x_inter = x1 + (float)(y_upper - y1) / (y2 - y1) * (x2 - x1); edge.slope_inverse = (float)(x2 - x1) / (y2 - y1); add_edge(edges, &num_edges, edge.y_upper, edge.x_inter, edge.slope_inverse); } int scan_line_y = edges[0].y_upper; while (scan_line_y >= edges[num_edges - 1].y_upper) { int num_intersections = 0; float intersections[num_points]; for (i = 0; i < num_edges; i++) { if (scan_line_y < edges[i].y_upper) continue; if (edges[i].slope_inverse == 0) continue; intersections[num_intersections++] = edges[i].x_inter; } sort(intersections, intersections + num_intersections); for (i = 0; i < num_intersections; i += 2) { int x_start = intersections[i]; int x_end = intersections[i + 1]; for (j = x_start; j < x_end; j++) { putpixel(j, scan_line_y, color); } } scan_line_y--; for (i = 0; i < num_edges; i++) { if (scan_line_y < edges[i].y_upper) { edges[i].x_inter += edges[i].slope_inverse; } } } } int main() { int poly_points[] = {100, 100, 200, 50, 300, 100, 250, 200, 150, 200}; int num_points = sizeof(poly_points) / sizeof(int) / 2; int color = YELLOW; initwindow(640, 480); fill_polygon(poly_points, num_points, color); getch(); closegraph(); return 0; } ``` 注意:该代码仅供参考,实际应用中仍需根据具体情况进行调整和优化。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值