我正在解决一个问题,其中有一个包含r行和c列的网格。我们从左上角的单元格开始,到右下角的单元格结束。限制是我们一次只能移动一次单元格,向下或向右移动。还有一些细胞可能是黑色的。问题是找到总数没有。我们可以从源头到目标的方式。
这是我的解决方案,它很简单但是在指数时间内运行:
int count(boolean[][] array, int r, int c)
{
if ((r < 0 || c < 0) || !array[r][c]) return 0;
if (r == 0 && c == 0) return 1;
return count(array, r - 1, c) + count(array, r, c - 1);
}
我遇到的问题是记住这个问题。
答案 0 :(得分:3)
记忆能否使这个解决方案更有效率?
是!
如果是这样,那么我就不能将路径中失败的所有单元列入黑名单,因为可能有其他路径通过那些可能导致目标的单元格。
正确。
所以我很困惑,因为我应该在这里缓存什么,以及在哪里添加额外的检查以避免检查我已经经历过的路径。
这是你做的。
制作一个可以为空的整数的r x c 2-d数组,我们称之为a
。数组的含义是“a[x][y]
给出从(x,y)到(r-1,c-1)的路径数” - 这假设(r-1,c-1)是我们试图去的“退出”单元格。
数组将从每个元素null开始。那很棒。 Null的意思是“我不知道”。
用零填充数组中的每个“被阻止”单元格。这意味着“没有办法从这个单元格到达出口”。
如果a[r-1][c-1]
为零,则退出被阻止,我们就完成了。每个查询的答案都是零,因为没有办法到达出口。我们假设退出单元格未被阻止。
有一种方法可以从退出单元格到达自身,因此请将a[r-1][ c-1]
填入1。
现在算法继续这样:
(x, y)
开始的解决方案。[x][y]
处的数组a[x][y]
。 让我们举个例子吧。假设我们有
n n n
n n 0
n n 1
我们被要求提供(0,1)的解决方案。我们没有解决方案。所以我们试图找到(1,1)和(0,2)的解决方案。
我们没有(1,1)的解决方案。所以我们必须得到(1,2)和(2,1)的解决方案。
(1,2)我们得到了。它是0。
(2,1)我们没有但是(2,2)我们这样做,那是唯一的邻居。 (2,2)是1,所以我们填写(2,1):
n n n
n n 0
n 1 1
现在我们有足够的信息来填写(1,1):
n n n
n 1 0
n 1 1
我们还没有完成(0,2)。它有一个零的邻居,所以是:
n n 0
n 1 0
n 1 1
现在我们可以填写(0,1)
n 1 0
n 1 0
n 1 1
我们正在寻找的是什么,所以我们已经完成了。
替代解决方案:预先计算阵列。
示例:
第一步:
n n n
n n 0
n n 1
填写外部行和列:
n n 0
n n 0
1 1 1
填写下一行和一列:
n 1 0
2 1 0
1 1 1
最后一次:
3 1 0
2 1 0
1 1 1
我们已经完成了;整个问题都解决了。
如果没有列入黑名单的单元格,那么我想知道这些备忘录是否有任何用途。
如果没有列入黑名单的单元格,则数组如下所示:
20 10 4 1
10 6 3 1
4 3 2 1
1 1 1 1
这是您之前应该看到的形状,并且知道如何直接计算每个元素。提示:你通常把它看成是一个三角形而不是正方形。