扫描线 窗内的星星

2024-05-11 00:58
文章标签 星星 扫描线 窗内

本文主要是介绍扫描线 窗内的星星,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

题目链接:248. 窗内的星星

算法分析

经过亚特兰蒂斯 那题的洗礼,这道扫描线题目就显得简单多了。但是很多细节还是得注意。这里只说细节。

1.边框上的星星不算怎么处理。如下图:
在这里插入图片描述

左下角的星星坐标为 ( x , y ) (x,y) (x,y),如果边框上的星星算的话,那么整个蓝色区域都可以放置边框的右上角顶点。如果不算的话,因为星星的坐标都是整数,边框的宽高也是整数,所以可放置的区域范围大致如上图绿色,在绿色区域中放置边框的右上角顶点,该星星肯定能被包含在内。

假设就这一颗星星,为了方便计算,可以考虑将星星和区域整体向下向左移动 0.5 0.5 0.5的距离,这样的话,星星的坐标变成了 ( x − 0.5 , y − 0.5 ) (x-0.5,y-0.5) (x0.5,y0.5),区域的左下角坐标为 ( x , y ) (x,y) (x,y),右上角坐标为 ( x + w − 1 , y + h − 1 ) (x+w-1,y+h-1) (x+w1,y+h1)。如果是多颗星星的话,也可以假设整体移动。因为我们扫描线关心的是这些区域,所以丝毫不影响结果。就算不移动,将区域的坐标按实型处理,也是可以的。

2.每个矩形区域的右边界的 x x x坐标为什么是 x + w x+w x+w,而不是 x + w − 1 x+w-1 x+w1。我们要讨论的问题是:在平面上有若干个区域,每个区域都带有一个权值,这些区域是交叉的,问在哪个坐标上,所有的权值和最大?
在这里插入图片描述
该区域左边界的横坐标是 x x x,一直到 x + w − 1 x+w-1 x+w1都是该区域的范围,因为数据都是整数,所以在 x + w x+w x+w处,该区域的影响才结束,因此,右边界是 x + w x+w x+w

3.区域的边界排序问题。排序如下:

bool cmp(ScanLine a, ScanLine b)
{if (a.x != b.x) return a.x < b.x;else return a.c < b.c;  // 
}

为什么横坐标相等的情况下,要按照亮度由小到大排序?假设当前值是ans,先加再减肯定比先减再加所产生的最大值要大,这样不应该是由大到小排序吗?其实如果 c = − 1 c=-1 c=1的话,是要减去的, c = 1 c=1 c=1的话是要加上去的,在扫描到某个位置的时候,应该先减掉,然后再加上,否则会造成错误。

4.数组的大小要注意。

#include <iostream>
#include <cstdio>
#include <cmath>
#include <algorithm>
#include <cstring>
#include <map>
#include <vector>
using namespace std;
#define ll long long 
const int N = 1e4 + 10;
struct ScanLine
{ll x, y1, y2, c;
}st[2*N];
ll lsh[4*N], val[4*N];
ll a[4*N];
map<ll, ll> lshval;
struct Segmentree
{ll dat, lazy;
}tr[16*N];
ll re()
{ll x = 0, f = 1; char c = getchar();while (c < '0' || c > '9') {if (c == '-') f = -1; c = getchar();} while (c >= '0' && c <= '9') {x = x * 10 + c - '0'; c = getchar();}return x * f; 
}
ll n, w, h;
void szbuild(int p, int l, int r)
{tr[p].dat = tr[p].lazy = 0; if (l == r) return;int mid = (l + r) >> 1;szbuild(2 * p, l, mid);szbuild(2 * p + 1, mid + 1, r);
}
void pushdown(int p, int al, int ar)
{if (al == ar) return;  // 叶节点无需下传 if (tr[p].lazy){tr[2*p].dat += tr[p].lazy;tr[2*p+1].dat += tr[p].lazy;tr[2*p].lazy += tr[p].lazy;tr[2*p+1].lazy += tr[p].lazy;tr[p].lazy = 0;} 
}
void szchange(int p, int al, int ar, int ql, int qr, int v)
{if (ql > ar || qr < al) return;if (ql <= al && ar <= qr){tr[p].dat += v;tr[p].lazy += v;return;}pushdown(p, al, ar);int mid = (al + ar) >> 1;szchange(2 * p, al, mid, ql, qr, v);szchange(2 * p + 1, mid + 1, ar, ql, qr, v);tr[p].dat = max(tr[2*p].dat, tr[2*p+1].dat);
}
bool cmp(ScanLine a, ScanLine b)
{if (a.x != b.x) return a.x < b.x;else return a.c < b.c;  // 
}
int main()
{while (~scanf("%lld%lld%lld", &n, &w, &h)){ll x, y, c;int t = 0;memset(st, 0, sizeof(st));memset(a, 0, sizeof(a));memset(lsh, 0, sizeof(lsh));lshval.clear();for (int i = 1; i <= 2 * n; i += 2){x = re(); y = re(); c = re();st[i].x = x;           st[i].y1 = y;   st[i].y2 = y + h - 1;   st[i].c = c;st[i+1].x = x + w;     st[i+1].y1 = y; st[i+1].y2 = y + h - 1; st[i+1].c = -c;a[++t] = y; a[++t] = y + h - 1;// 小细节 st[i+1].x是x+w,不是x+w-1   }// 离散化 sort(a + 1, a + t + 1);memcpy(lsh, a, sizeof(a));int cnt = unique(lsh + 1, lsh + t + 1) - lsh - 1;for (int i = 1; i <= t; ++i){ll tem = a[i];  // 原数  注意a[]会超int  a[i] = lower_bound(lsh + 1, lsh + cnt + 1, a[i]) - lsh;val[a[i]] = tem;//	lshval[tem] = a[i];  // 超下标上限了 lshval[tem] = a[i]; }		// 建线段树,维护的是纵坐标,总共有cnt个  szbuild(1, 1, cnt);// 扫描进行区间修改和更新答案 ll ans = -1;sort(st + 1, st + 2 * n + 1, cmp); for (int i = 1; i <= 2 * n; ++i){ll ql = lshval[st[i].y1], qr = lshval[st[i].y2], c = st[i].c;//	ll ql = lower_bound(lsh + 1, lsh + cnt + 1, st[i].y1) - lsh;//	ll qr = lower_bound(lsh + 1, lsh + cnt + 1, st[i].y2) - lsh;//	ll c = st[i].c;szchange(1, 1, cnt, ql, qr, c);ans = max(ans, tr[1].dat);} printf("%lld\n", ans);}return 0;
}

反思与总结

  1. 如果有部分需要开long long,那就干脆全部都开long long。这个题目就是在离散化的时候 t e m tem tem变量定义成了 i n t int int,导致溢出,调了很久。

  2. 多组数据的时候,打扫战场要彻底。

这篇关于扫描线 窗内的星星的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/978062

相关文章

洛谷P5490扫描线

0是最小的数字,将一个线段看成一个区间,对于一个矩形,从下扫到上,入边为1,而出边为-1,意思是将这个区间上的所有点加1(区间修改).把线段表示为Line[i],其中记录了l,r,h,tag,左右端点,高度,入边还是出边(1或-1) 那么每次区间修改后不为0的区间它的值可能是1,2,3或者是其它数字,这不好统计,可以将它转化一下,0是不是表示没有被覆盖过的地方,我们只要统计0的个数然后用总长减去

Zxing扫描二维码精简(竖屏、拉伸处理、扫描框大小和扫描线移动、开灯)

自己在简版zxing的基础上美化了下,给大家分享下,直接扫描功能没问题。 就是从相册导入图片,解码一直不成功,导入图片解码 我引入了一个解码类  RGBLuminanceSource(百度网上二维码图片解码都是这个,我还把这个类编译了,导到core.jar包里面了), 好像是hity类型有问题,反正一直不成功。 有大神解决了,回复告诉我!谢谢! 分享 暂时没做效

扫描线Sweep Line算法总结

扫描线算法,推荐还是用标准的模板去写,treemap只适合于求最大的overlap个数的题目,其余的不能用treemap来解,所以推荐还是用event的思想去+1, -1然后排序扫描的方法可以用来解所有的题型; Number of Airplanes in the Sky 思路:经典扫描线算法:把interval起飞和降落做为event,全部打散,按照时间排列,同时时间相等的,按照降落在前面,起

宇宙星星转动特效带背景音乐引导页源码

源码介绍 宇宙星星转动特效带背景音乐引导页源码,源码由HTML+CSS+JS组成,记事本打开源码文件可以进行内容文字之类的修改,双击html文件可以本地运行效果,也可以上传到服务器里面,重定向这个界面 效果预览 源码获取 宇宙星星转动特效带背景音乐引导页源码

矩形面积并、矩形面积交、矩形周长并(线段树、扫描线总结)

转自:http://blog.csdn.net/lwt36/article/details/48908031 HDU 1542 [POJ 1151] Atlantis (矩形面积并) 题意: 求N<=100个矩形的面积并 分析: 离散化: 这些技巧都是老生常谈的了, 不然浮点数怎么建树, 离散化 x 坐标就可以了扫描线: 首先把矩形按 y 轴分成两条边, 上边和下边,

【HDU1255】【线段树】【扫描线】【面积】

覆盖的面积 Time Limit: 10000/5000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others) Total Submission(s): 4417    Accepted Submission(s): 2180 Problem Description 给定平面上若干矩形,求出被这些矩形覆

poj2482--Stars in Your Window(扫描线)

题目链接:点击打开链接 链接题目大意:给出n个星星的坐标,每个星星有一个亮度,给出一个矩形的长和宽,问矩形能包括的星星的最大亮度和(不包括边框)。 假设每个星星都是矩形的最左下点,那么每一个星星都可以得到一个矩形,(x,y)->(x,y,x+w,y+h),这个矩形的两条高边的值也就是星星的亮度k和-k,对于不同的矩形来说,如果高线出现重合部分,那么也就是说这两个星是可以出现在同一个矩形中的,扫

HDU 1828 POJ 1177 Picture(线段树+扫描线+离散化)

HDU题目地址:HDU 1828  POJ题目地址:POJ 1177 这题是求周长并,我用的方法可能有点麻烦。。是先求横着的线,再求竖着的线。每次只要求出每次的总区间覆盖长度,然后每次累加这次的总区间覆盖与上次的总区间覆盖长度的差的绝对值。因为只有长度发生变化时,才会产生一段新的周长。 待会再试试只扫描一次的方法。此博客有待更新。 代码如下: #include <iostream>#

POJ 3277 City Horizon(线段树+扫描线+离散化)

题目地址:POJ 3277 水题。。稍微处理一下然后用求面积并的方法求即可。 代码如下: #include <iostream>#include <cstdio>#include <string>#include <cstring>#include <stdlib.h>#include <math.h>#include <ctype.h>#include <queue>#

HDU 1542 POJ 1151 Atlantis(线段树+扫描线)

题目地址:HDOJ地址:HDU 1542  POJ 地址:POJ 1151 第一发扫描线。。费了好大一番功夫。。构思用了半天。。写出来调试成功用了半天。。。真是弱渣。。 所谓扫描线就是从上往下或从下往上扫描,每到一个边,就进行增或删的处理。最后出来的值就是总的面积。对于求面积并的问题,可以参考这篇博客(博客地址),讲的不错。 具体实现过程是用lazy标记此时的边数量,如果大于0,说明这个地方