我们在2D平面上给出N(N <= 10 6 )点和整数D(N <= 10 6 ),我们想要找到两个点p1,p2(p1右侧的p2),使p1.y
和p2.y
之间的差异至少为D,p2.x - p1.x
最小化。
x轴和y轴的范围为0..10 6
这是来自USACO过去比赛的problem。
这是我尝试解决它:
MAXY = N个点中的最大y轴
假设我们知道p1,那么我们很容易找到p2;通过获取y轴在p1.y+D
到MAXY范围内或在0到p1.y-D
范围内的所有点,并取x轴最小的点大于p.x
。这将是p2的最佳选择。
但是由于我们不知道p1,我们必须尝试p1的所有点,因此找到p2的最佳选择应该有效地完成。
我使用了一个分段树。树中的每个节点都将按照x轴的排序顺序存储相应范围内的所有点。在查询时,如果一个节点落在查询范围内,那么我们在数组上二进制搜索p1.x
并返回大于它的最小元素。
对于p1的每个选择,我们使用范围0,p1.y-D和p1.y + D,MAXY两次查询树,并取出返回的两个点中的最佳值。
树的构建可以在O(NlogN)时间内完成。 每个查询都需要O(logN * logN)时间,我们进行N次查询,因此所花费的总时间为(Nlogn * logn),可能不会在2秒的时间限制内运行。 (10 6 * 20 * 20)。 此外,所占用的内存为O(NlogN),约为80 mb(100000 * 20 * 4 kb),超出限制为64 mb。
我们如何更快地进行查询并使用更小的空间?
答案 0 :(得分:5)
可以更轻松地完成。
假设您有两个数组副本:一个按Y轴排序,另一个按X轴排序。现在,您将遍历Y排序的数组,并且对于每个点(让它命名为cur),您应该在X排序的数组中二进制搜索适当的点(具有最小的p2.x - p1.x)。如果二进制搜索会找到相同的点或Y坐标小于cur + D的点,你应该从X排序的数组中删除该点(我们再也不需要在X排序的数组中的那个点,因为我们只增加Y坐标)并再次运行二进制搜索。答案将是二进制搜索结果中最小的。
由于我们需要快速计时,我们应该快速从阵列中擦除点。它可以通过使用二叉树来完成 - 它可以在O(logN)时间内擦除任何节点,并且可以在O(logN)时间内进行二进制搜索。当您从树中删除每个节点一次并且需要O(logN + logN)时间时 - 总时间将为O(N * logN)。预处理时间也是O(N * logN)。所采用的存储器也将是O(N)。
顺便说一句,你的解决方案也是合适的,因为实际N是10 ^ 5而不是10 ^ 6。这允许您的解决方案将时间保持在2秒以下,并使用少于20MB的内存。
答案 1 :(得分:1)
排序&amp; amp;扫描。
按x排序,因为您希望通过x找到最小差异。它需要O(N logN)时间。
从x的头部维护两个索引i和j。
首先越快,找到| P [i] .y - P [j] .y |的位置&GT; d
和X = | P [i] .x - P [j] .x |是您的第一个选择。
然后通过向前移动索引来更新X.尝试P [i + 1],从P [i + 2]扫描为P [j]并增加直到| P [i] .x - P [j] .x | &gt; = X.如果有可用的新X,请将其设置为X。
这可能首先会做很多比较。但是,既然你更新你的X,不知何故会使你的比较范围缩小。