如何快速找到最佳炸弹落差区?

时间:2013-12-28 19:27:12

标签: algorithm language-agnostic

我得到了一份类似这样的作业:

  

您将获得由四种颜色的像素组成的图像。颜色对应于地形,敌人,盟友和墙壁。可以在任何坐标(一对整数)处放下炸弹。你也得到了:

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

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

     

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

     

你的得分是0。找到你应该放下一颗炸弹以获得最佳分数的坐标。如果有多个最佳解决方案,请返回其中任何一个。

这是一个裁剪,调整大小并改变颜色以提高可读性的示例图像:

  

Example four-colored image

我可以找到原始图像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

当然,可以优化此'代码段',但在此表单中更容易理解。通过用墙壁约束高度图圆圈,它可以扩展到完整的解决方案。绘制圆圈很简单,许多图像处理库提供了执行此操作的功能,因此绘制圆形,然后在墙上绘制墙壁以及从中心停止在墙壁或圆形边界上的填充圆圈可能是个好主意。我将在实施时检查性能。

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

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

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

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

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


3 个答案:

答案 0 :(得分:7)

这是一个部分答案,我希望能引发一些讨论

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

  

如果没有墙壁会有什么好处?

进一步将其减少到

  

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

甚至更进一步,

  

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

相当于说

  

给定一组点,定位一个单位磁盘以覆盖可能的最大点数。

冷却。这感觉就像一个很好的,坚实的,与领域无关的问题,许多人肯定会遇到过这个问题。一些快速的谷歌搜索和wahey,我们找到了相当多的相关资源,包括this SO question

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

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

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

对心理测试任何候选算法的三种方案:

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

  2. 在地图上有单一直墙的情况下,找到在单位距离和视线范围内同时最大化点数的位置。

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

答案 1 :(得分:1)

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

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

  • 以各自的敌人/盟友成本累计所有这些圈子,得分最高的像素是您的解决方案

您可以做的一个优化是:

  • 将敌方目标存储在快速空间数据结构中,例如KD树
  • 对于每个敌人,找到距离2 * r
  • 内的邻近敌人的数量
  • 按敌人的数量排序敌人,降序
  • 查看敌人列表,从与最邻居的敌人开始,并且仅围绕当前敌人构建半径为2 * r的累加器地图。
  • 如果当前的敌人有n个邻居,这意味着你可以与这个敌人达到最多(n + 1)* e的分数,假设你也击中了他的所有邻居而没有盟友。因此,如果剩下的敌人没有足够的邻居来获得当前最高分,那么你可以停止搜索。

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

在固定半径内找到所有最近邻居是一个研究得很好的问题,在python中应该有几个有效的实现。

答案 2 :(得分:0)

这样可以更有效地做到这一点: -

  1. 扫描网格中的士兵
  2. 保持网格中每个点的得分最初为零
  3. 对于每个士兵,计算以士兵和半径r为中心的圆周上的所有点
  4. 穿过光线加入士兵并指向周围。
  5. 检查射线加入士兵的当前点是否有障碍物并且在圆周上指向
  6. 如果未找到障碍物,则根据士兵将e或a添加到当前点的分数并移至下一个。
  7. 否则打破并继续下一个圆周
  8. 维持在更新分数时获得的最高分数以及分数。
  9. 结束时的分数和分数将是最佳下降区
  10. 时间复杂度: -

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

    查找周长: - O(r)

    导线射线: - O(r)

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

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