我有一个nxm个由非负整数组成的矩阵。例如:

2 3 4 7 1
1 5 2 6 2
4 3 4 2 1
2 1 2 4 1
3 1 3 4 1
2 1 4 3 2
6 9 1 6 4

“投下炸弹”会使目标细胞及其所有八个邻居的数量减少一个,直到最小值为零。

x x x 
x X x
x x x

什么样的算法可以确定将所有细胞减少到零所需的最少炸弹数量?

B选项(因为我不是一个细心的读者)

事实上,问题的第一个版本并不是我要寻找的答案。我没有仔细阅读整个任务,有额外的约束条件,让我们说:

那么简单的问题是,当行中的序列必须是非递增的:

8 7 6 6 5是可能的输入序列

7 8 5 5 2是不可能的,因为7 -> 8在一个序列中增长。

也许为“简单”的问题找到答案会有助于为更难的问题找到解决方案。

PS:我相信当我们有几个相同的情况需要最少的炸弹来清除上面的线时,我们会选择在“左侧”使用最多炸弹的一个。还有什么证据是正确的吗?


当前回答

为了尽量减少炸弹的数量,我们必须最大化每个炸弹的效果。要做到这一点,每一步我们都要选择最好的目标。对于每一个点,它和它的八个邻居的总和,可以被用作轰炸这一点的效率量。这将提供接近最佳的炸弹序列。

UPD:我们还应该考虑到零的数量,因为轰炸它们效率很低。事实上,问题是最小化击中零的数量。但我们不知道每一步如何使我们更接近这个目标。我同意这个问题是np完全的。我建议用贪婪的方法,它会给出一个接近真实的答案。

其他回答

Pólya说:“如果你不能解决一个问题,那么有一个更容易解决的问题:找到它。”

显然更简单的问题是一维问题(当网格是单行时)。让我们从最简单的算法开始——贪婪地轰炸最大的目标。什么时候会出问题?

给定11 11,贪婪算法对先炸毁哪个单元格无关。当然,中心单元格更好——它一次将所有三个单元格归零。这就提出了一种新的算法a,“炸弹最小化剩余的总和”。这个算法什么时候会出错?

给定1 1 2 11 1,算法A在轰炸第2,第3或第4单元格之间是无所谓的。但是轰炸第二个单元格,留下0 0 11 11比轰炸第三个单元格,留下10 10 10 10 1好。如何解决这个问题?轰炸第三个单元格的问题是,左边的功和右边的功必须分开做。

“炸弹使剩余的总和最小化,但使左边(我们轰炸的地方)的最小值和右边的最小值最大化”如何?叫这个算法b,这个算法什么时候出错?


编辑:在阅读了评论之后,我同意一个更有趣的问题将是改变一维问题,使其两端连接起来。很乐意看到这方面的进展。

这是部分答案,我试图找到一个下界和上界,可能是炸弹的数量。

在3x3和更小的板上,解决方案通常是编号最大的单元。

在大于4x4的板中,第一个明显的下界是角的和:

*2* 3  7 *1*
 1  5  6  2
 2  1  3  2
*6* 9  6 *4*

无论你如何安排炸弹,都不可能用少于2+1+6+4=13个炸弹来清除这个4x4板。

在其他回答中已经提到,将炸弹放置在第二个角落以消除角落并不比将炸弹放置在角落本身更糟糕,所以考虑到棋盘:

*2* 3  4  7 *1*
 1  5  2  6  2
 4  3  4  2  1
 2  1  2  4  1
 3  1  3  4  1
 2  1  4  3  2
*6* 9  1  6 *4*

我们可以通过在第二个角上放置炸弹来将角归零,从而得到一个新的板:

 0  1  1  6  0
 0  3  0  5  1
 2  1  1  1  0
 2  1  2  4  1
 0  0  0  0  0
 0  0  0  0  0
 0  3  0  2  0

到目前为止一切顺利。我们需要13枚炸弹才能清空角落。

现在观察下面标记的数字6、4、3和2:

 0  1  1 *6* 0
 0  3  0  5  1
 2  1  1  1  0
*2* 1  2 *4* 1
 0  0  0  0  0
 0  0  0  0  0
 0 *3* 0  2  0

我们无法使用一枚炸弹去轰炸任何两个细胞,所以最小炸弹数量增加了6+4+3+2,所以再加上我们用来清除角落的炸弹数量,我们得到这张地图所需的最小炸弹数量变成了28枚炸弹。用少于28个炸弹是不可能清除这张地图的,这是这张地图的下限。

可以用贪心算法建立上界。其他答案表明,贪婪算法产生的解决方案使用28个炸弹。因为我们之前已经证明了没有一个最优解可以拥有少于28个炸弹,所以28个炸弹确实是一个最优解。

当贪心和我上面提到的寻找最小界的方法不收敛时,我猜你必须回去检查所有的组合。

求下界的算法如下:

选一个数值最大的元素,命名为P。 将所有距离P和P本身两步远的单元格标记为不可拾取。 将P添加到最小值列表中。 重复步骤1,直到所有单元格都不可拾取。 对最小值列表求和得到下界。

有一种方法可以把这个问题简化为一个简单的子问题。

解释分为两部分,算法和算法的原因 提供最优解决方案。没有第二个,第一个就说不通了,所以我 从为什么开始。

如果你想轰炸矩形(假设一个大矩形-还没有边缘情况) 你可以看到,只有这样才能减少空心矩形上的正方形 周长到0的意思是炸毁周长或者炸毁的空心矩形 就在外围的方块里。我称周长为图层1,其中的矩形为图层2。

一个重要的观点是,没有点轰炸层1,因为 你这样做得到的“爆炸半径”总是包含在爆炸半径内 另一个来自第2层的正方形。你应该很容易就能说服自己。

所以,我们可以把问题简化为找到一个最优的方法来炸开周长,然后我们可以重复这个过程,直到所有的平方都为0。

但当然了,如果有爆炸的可能,并不总能找到最优解 以一种不太理想的方式远离周边,但通过使用X个额外的炸弹制造 用>X炸弹减少内层的问题。如果我们调用 第一层,如果我们在第二层的某个地方放置一个额外的X炸弹(只是 在第1层内,我们可以减少之后轰炸第2层的努力吗 X ?换句话说,我们必须证明我们可以贪心化简外部 周长。

但是,我们知道我们可以贪婪。因为第2层的炸弹永远不会更多 有效减少第2层到0比战略上放置炸弹在第3层。和 因为和之前一样的原因-总有一个炸弹我们可以放在第3层 将影响第2层的每一个方块,炸弹放在第2层可以。所以,它可以 永远不要伤害我们的贪婪(在这个意义上的贪婪)。

所以,我们要做的就是找到最优的方法,通过轰炸将许可值降为0 下一个内层。

我们永远不会因为先把角落炸到0而受伤,因为只有内层的角落可以到达,所以我们真的没有选择(并且,任何可以到达角落的周长炸弹的爆炸半径都包含在内层角落的爆炸半径中)。

一旦我们这样做了,与0角相邻的周长上的正方形只能由内层的2个正方形到达:

0       A       B

C       X       Y

D       Z

在这一点上,周长实际上是一个封闭的1维环,因为任何炸弹都会减少3个相邻的正方形。除了角落附近的一些奇怪之处——X可以“击中”A、B、C和D。

Now we can't use any blast radius tricks - the situation of each square is symmetric, except for the weird corners, and even there no blast radius is a subset of another. Note that if this were a line (as Colonel Panic discusses) instead of a closed loop the solution is trivial. The end points must be reduced to 0, and it never harms you to bomb the points adjacent to the end points, again because the blast radius is a superset. Once you have made your endpoint 0, you still have a new endpoint, so repeat (until the line is all 0).

所以,如果我们可以优化地将层中的单个正方形减少到0,我们就有了一个算法(因为我们已经切断了循环,现在有了一条带有端点的直线)。我相信轰炸与最小值相邻的正方形(给你2个选项),这样在最小值的2个正方形内的最大值就是最小值(你可能不得不分割你的轰炸来管理这一点)将是最优的,但我还没有证明。

在这里,线性规划方法似乎非常有用。

设Pm x n为包含位置值的矩阵:

现在定义一个炸弹矩阵B(x, y)m x n,其中1≤x≤m, 1≤y≤n如下所示

以这样一种方式

例如:

所以我们正在寻找一个矩阵Bm x n = [bij]

可以定义为炸弹矩阵的和: (qij将是我们在pij位置投放的炸弹数量) pij - bij≤0(为了更简洁,我们称之为P - B≤0)

而且,B应该使和最小。

我们也可以把B写成前面的丑矩阵:

由于P - B≤0(即P≤B),我们得到了如下线性不等式系统:

qmn x1定义为

PMN x 1定义为

我们可以说我们有一个方程组是smnxmn这个矩阵要倒转来解方程组。我自己没有扩展它,但我相信在代码中应该很容易做到。

现在,我们有一个最小的问题可以表述为

I believe it is something easy, almost trivial to be solved with something like the simplex algorithm (there is this rather cool doc about it). However, I do know almost no linear programming (I will take a course about it on Coursera but it is just in the future...), I had some headaches trying to understand it and I have a huge freelance job to finish so I just give up here. It can be that I did something wrong at some point, or that it can't go any further, but I believe this path can eventually lead to the solution. Anyway, I am anxious for your feedback.

(特别感谢这个神奇的网站从LaTeX表达式创建图片)

所有这些问题都归结为计算编辑距离。简单地计算给定矩阵和零矩阵之间的Levenshtein距离的变体,其中编辑被轰炸替换,使用动态编程来存储中间数组之间的距离。我建议使用矩阵的哈希作为键。在pseudo-Python:

memo = {}

def bomb(matrix,i,j):
    # bomb matrix at i,j

def bombsRequired(matrix,i,j):
    # bombs required to zero matrix[i,j]

def distance(m1, i, len1, m2, j, len2):
    key = hash(m1)
    if memo[key] != None: 
        return memo[key]

    if len1 == 0: return len2
    if len2 == 0: return len1

    cost = 0
    if m1 != m2: cost = m1[i,j]
    m = bomb(m1,i,j)
    dist = distance(str1,i+1,len1-1,str2,j+1,len2-1)+cost)
    memo[key] = dist
    return dist