【问题标题】:How to quickly find optimal bomb drop zone?如何快速找到最佳投弹区?
【发布时间】:2014-01-16 02:15:59
【问题描述】:

我的作业是这样的:

给你一张由四种颜色的像素组成的图像。颜色对应于地形、敌人、盟友和墙壁。可以在任何坐标(一对整数)处投下炸弹。还给你:

  • r - 炸弹的作用半径(以像素为单位,正整数)
  • e - 杀死一个敌人的点数
  • a - 杀死一个盟友的点数

(例如r = 10e = 1a = -2

当炸弹被投下时,半径(欧几里得距离)内的所有敌人和盟友都会被杀死,除非他们和炸弹之间有墙(即连接炸弹的士兵的非抗锯齿线穿过墙壁)。当炸弹落在墙上时,这个特定像素的行为就像正常地形一样。墙的其余部分仍然是墙。

你从 0 分开始。找到你应该投下一颗炸弹的坐标以获得最好的分数。如果有多个最优解,则返回其中任何一个。

这是一个示例图片,经过裁剪、调整大小并更改了颜色以提高可读性:

我得到的原图可以在here找到。

我已经解决的问题

我知道暴力破解这个问题是一个糟糕的解决方案。我想出了一个想法,如何在没有墙壁的情况下快速解决它。这是一些伪代码:

args Map, R, A, E

for (every Soldier)
    create a Heightmap with dimensions of Map
    zero-fill the Heightmap
    on the Heightmap draw a filled circle of value 1 around Soldier with radius R

    if (Soldier is Ally)
        multiply Heightmap by A
    else
        multiply Heightmap by E

add all Heightmaps together
return coordinates of highest point in TotalHeightmap

当然这个'sn-p'是可以优化的,但是这种形式更容易理解。通过用墙壁约束高度图圆圈,可以将其扩展到完整的解决方案。画圆很简单,许多图像处理库都提供了这样做的函数,所以画圆可能是个好主意,然后在上面画墙,然后从中心填充圆,停在墙或圆的边界上。我会在实施时检查性能。

如果没有约束圈,我会这样做:

run the above code to get a TotalHeightmap
create empty PointList

for (every Point in TotalHeightmap)
    create PointObject with properties:
        Coordinates,
        Height,
        WallsFlag = False
    add PointObject to PointList

sort PointList by descending Height

until (PointList[0].WallsFlag == True)
    for (every Soldier in radius R from PointList[0])
        if (Bresenham line connecting Soldier with PointList[0] intersects a Wall)
            subtract (A if Soldier is Ally else E) from PointList[0].Height

    set PointList[0].WallsFlag = True
    sort PointList by descending Height

return PointList[0].Coordinates

只要敌人和盟友的分数都不是负数,它就会起作用,所以它远非完美。为了解决这个问题,我可以遍历所有像素,但这会非常慢(我猜,它不像暴力破解那么慢,但这听起来不是一个好主意)。寻找墙壁交叉点的方法似乎也很粗糙。

我正在寻找一个更优雅和快速的解决方案来解决这个问题。你会怎么解决?如果有帮助,我将在 Python 中使用 PIL 实现它。

顺便说一句,我相信我的老师同意我在 SO 上发布这个问题,我相信他甚至希望我讨论它并实施最佳解决方案。


【问题讨论】:

  • 是什么让您认为分配高度图、相乘和相加比简单的暴力破解更快?另外,slow的定义是什么?只有相对于更快的替代方案,事情才会变得缓慢。你的硬件速度慢吗?你甚至对速度有限制吗?在这项任务中有这么多条件,可能没有什么比蛮力更精确和可靠了。
  • @Haroogan 正如我所说,我知道它可以优化。高度图只是解释我的算法的好方法。我的老师说它必须足够快,以平均笔记本电脑在一分钟内解决example map。即使使用蛮力方法也应该可以实现,但我就是无法忍受粗鲁、盲目的解决方案。我正在寻求一个很好的、优雅的算法来解决这个问题或关于如何改进我的解决方案的建议。
  • 太多不可预测的情况。墙可以有任何几何形状。您将不得不蛮力进行:测试每个像素周围半径范围内敌人的存在,并将来自敌人/盟友的射线发射到该像素,以找出与墙壁的交叉点(如果有的话)以拒绝它们。这似乎是获得可靠结果的唯一方法。而且恐怕没有典型的数学理论能够以优雅的方式解决这个问题。将其视为光线追踪,这是纯粹的蛮力(除了优化)。
  • 这个任务的一个优点是它可以很容易地被并行化。例如,您可以简单地将 OpenMP 用于循环。或者,您可以使用所需的模式手动细分域,并在单独的线程中运行每个子域的处理(正是 OpenMP 对循环所做的事情,但麻烦较少)。您可以这样做,因为每个测试的结果不相互依赖,从这个意义上说,任务非常简单。例如,任何现代 GPU 都绝对会完成这项任务。只需为您的利益使用硬件,您就赢了。
  • 这听起来很棒codegolf

标签: algorithm language-agnostic


【解决方案1】:

以下是部分答案,希望能引发一些讨论

解决任何问题的第一条规则是找到一个更容易的问题。在这种情况下,我们可以问:

如果没有墙,什么是好的解决方案?

并进一步减少到

如果没有墙壁或敌人,什么是好的解决方案?

更进一步,

如果没有墙壁或敌人并且炸弹的半径为 1,那么有什么好的解决方案?

相当于说

给定一组点,放置一个单位圆盘以覆盖尽可能多的点。

酷。这感觉像是一个不错的、可靠的、独立于域的问题,很多人肯定以前都遇到过。一些快速的谷歌搜索和哇,我们找到了很多相关的资源,包括this SO question

在那个问题中,公认的答案有一个重要的观察:如果我们有一个覆盖最大点数的圆盘,我们可以移动那个圆盘以获得另一个边缘至少有两个点的圆盘。因此,如果我们取彼此距离为 2 内的每一对点,并通过该对点构造两个单位圆(对于 O(n^2) 个圆),那么其中一个圆是保证包含尽可能多的点数。

这可以很容易地适应您的问题的“无墙”版本。虽然它会是 O(n^3) 天真(可能是 O(n^2) 个圆圈,并且每个圆圈内可能有 n 个点) ,它可能比典型问题实例中的速度快得多。但是,如果您想聪明一点,请研究固定半径最近邻问题(我能找到的最好的论文是 here,但不幸的是没有公开版本)。

我们如何引入墙?如果一个圆盘与一面墙相交,我们不能可靠地移动它,以使两个点位于边缘,同时保持相同的分数。我会考虑一下,希望其他人在此期间会有一些想法。

心理测试任何候选算法的三个场景:

  1. 当地图上存在单个“像素”墙壁时,找到在单位距离和视线内同时最大化点数的位置。

  2. 当地图上只有一堵直墙时,找到在单位距离和视线内同时最大化点数的位置。

  3. 当墙壁在地图上形成一个单一的空心正方形时,找到在单位距离和视线内同时最大化点数的位置。

【讨论】:

    【解决方案2】:

    我认为您自己建议的算法是一个好方法:

    • 对于每个敌人/盟友,使用给定的墙壁,绘制该目标可能被炸弹击中的所有位置的部分模糊圆圈。

    • 将所有这些圆圈与各自的敌人/盟友成本加在一起,得分最高的像素就是你的解决方案

    你可以做的一个优化是这样的:

    • 将敌方目标存储在 KD 树等快速空间数据结构中
    • 对于每个敌人,求距离 2*r 内相邻敌人的数量
    • 按照邻居的数量降序排列敌人
    • 遍历敌人列表,从邻居最多的敌人开始,只在当前敌人周围2*r半径内构建累加器地图。
    • 如果当前敌人有 n 个邻居,这意味着你最多可以用这个敌人获得 (n+1)*e 的分数,假设你也击中了他的所有邻居并且没有盟友。因此,如果剩余的敌人没有足够的邻居来超越当前的最佳分数,您可以停止搜索。

    (假设击中盟友不会提高你的分数)

    在固定半径内查找所有最近的邻居是一个经过充分研究的问题,python 中应该有几种有效的实现。

    【讨论】:

      【解决方案3】:

      以下方法可能会更有效:-

      1. 扫描网格寻找士兵
      2. 保持网格中每个点的分数最初为零
      3. 为每个士兵计算圆周上的所有点,以士兵为中心,半径为 r
      4. 穿过射线加入士兵并指向圆周。
      5. 检查当前点是否有障碍物,射线加入士兵和圆周上的点
      6. 如果未找到障碍物,则根据士兵在当前点的得分上添加 e 或 a 并移动到下一个。
      7. 否则中断并继续圆周上的下一个点
      8. 在更新分数和分数时保持最高分数。
      9. 最后的分数和分数将是最佳下降区

      时间复杂度:-

      扫描网格:- O(N*M)

      求周长:-O(r)

      遍历射线:- O(r)

      每个士兵完成的工作:- O(r)*O(r) = O(r^2)

      总时间复杂度 = O(N*M + S*r^2) 其中 S 是士兵人数

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-07-28
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2022-01-06
        相关资源
        最近更新 更多