我在接受采访时向我询问了一个问题,这是我发现的类似问题,所以我想在这里问一下。问题是
机器人可以在N X N网格中的(1,1)位置,机器人可以向左,向右,向上和向下的任何方向移动。我也给了一个整数k,表示路径中的最大步数。我必须以k或更少的步长计算从(1,1)到(N,N)的可能方式的数量。
我知道如何解决这个问题的简化版本,只能向右和向下移动。这可以通过动态编程来解决。我尝试在这里使用相同的技术,但我不认为它可以使用二维矩阵解决,我尝试了一种类似的方法,从左或右或向右计算可能的方式数,并向下总结,但问题是我不知道从下行方向的数量也应该加上。所以我进入循环。我能够使用递归来解决这个问题,我可以递归(N,N,k)调用up,left和k-1,总结它们但我觉得这也不正确,如果它可能是正确的具有指数复杂性。我发现了与此类似的问题所以我想知道解决这些类型问题的最佳方法。
答案 0 :(得分:3)
假设您有一个NxN矩阵,其中每个单元格为您提供了从(1,1)到(i,j)的方式,精确步长为k(某些条目为零)。您现在可以创建一个NxN矩阵,其中每个单元格为您提供从(1,1)移动到(i,j)的方式的数量,精确地为k + 1步 - 从全零矩阵开始,然后添加在前一个矩阵的单元格(i,j)到单元格(i + 1,j),(i,j + 1),...等等。
每个k矩阵中的(N,N)条目为您提供了从(1,1)到(i,j)的完全k步的移动方式 - 您现在要做的就是添加它们一起来。
Here is an example for the 2x2 case, where steps outside the
matrix are not allowed, and (1,1) is at the top left.
In 0 steps, you can only get to the (1,1) cell:
1 0
0 0
There is one path to 1,1. From here you can go down or right,
so there are two different paths of length 1:
0 1
1 0
From the top right path you can go left or down, and from the
bottom left you can go right or up, so both cells have paths
that can be extended in two ways, and end up in the same two
cells. We add two copies of the following, one from each non-zero
cell
1 0
0 1
giving us these totals for paths of length two:
2 0
0 2
There are two choices from each of the non-empty cells again
so we have much the same as before for paths of length three.
0 4
4 0
Two features of this are easy checks:
1) For each length of path, only two cells are non-zero,
corresponding to the length of the path being odd or even.
2) The number of paths at each stage is a power of two, because
each path corresponds to a choice at each step as to whether to
go horizontally or vertically. (This only holds for this simple
2x2 case).
答案 1 :(得分:1)
更新:此算法不正确。请参阅评论和mcdowella的回答。但是,校正后的算法不会对时间复杂度产生影响。
至少可以在O(k * N ^ 2)时间内完成。伪代码:
# grid[i,j] contains the number of ways we can get to i,j in at most n steps,
# where n is initially 0
grid := N by N array of 0s
grid[1,1] := 1
for n from 1 to k:
old := grid
for each cell i,j in grid:
# cells outside the grid considered 0 here
grid[i,j] := old[i,j] + old[i-1,j] + old[i+1,j] + old[i,j-1] + old[i,j+1]
return grid[N,N]
可能存在更复杂的O(log k *(N * log N)^ 2)解决方案。通过外部for
循环的每次迭代都只是具有固定内核的卷积。因此,我们可以将内核与自身进行卷积,以获得将多个迭代融合为一个的更大内核,并使用FFT来计算卷积。
答案 2 :(得分:0)
基本上,唯一路径(行,列)= 0,如果行> N ||栏> ñ 1如果行== N&& column == N. uniquepaths(row + 1,column)+ uniquePaths(row,column + 1) 即,该解决方案具有最佳子结构和重叠的子问题。因此,它可以使用动态编程来解决。下面是它的记忆(懒惰/随需应变)版本(相关的基本上还返回路径:Algorithm for finding all paths in a NxN grid)(您可以参考我的博客了解更多详情:http://codingworkout.blogspot.com/2014/08/robot-in-grid-unique-paths.html)
private int GetUniquePaths_DP_Memoization_Lazy(int?[][] DP_Memoization_Lazy_Cache, int row,
int column)
{
int N = DP_Memoization_Lazy_Cache.Length - 1;
if (row > N)
{
return 0;
}
if (column > N)
{
return 0;
}
if(DP_Memoization_Lazy_Cache[row][column] != null)
{
return DP_Memoization_Lazy_Cache[row][column].Value;
}
if((row == N) && (column == N))
{
DP_Memoization_Lazy_Cache[N][N] = 1;
return 1;
}
int pathsWhenMovedDown = this.GetUniquePaths_DP_Memoization_Lazy(DP_Memoization_Lazy_Cache,
row + 1, column);
int pathsWhenMovedRight = this.GetUniquePaths_DP_Memoization_Lazy(DP_Memoization_Lazy_Cache,
row, column + 1);
DP_Memoization_Lazy_Cache[row][column] = pathsWhenMovedDown + pathsWhenMovedRight;
return DP_Memoization_Lazy_Cache[row][column].Value;
}
来电者
int GetUniquePaths_DP_Memoization_Lazy(int N)
{
int?[][] DP_Memoization_Lazy_Cache = new int?[N + 1][];
for(int i =0;i<=N;i++)
{
DP_Memoization_Lazy_Cache[i] = new int?[N + 1];
for(int j=0;j<=N;j++)
{
DP_Memoization_Lazy_Cache[i][j] = null;
}
}
this.GetUniquePaths_DP_Memoization_Lazy(DP_Memoization_Lazy_Cache, row: 1, column: 1);
return DP_Memoization_Lazy_Cache[1][1].Value;
}
单元测试
[TestCategory(Constants.DynamicProgramming)]
public void RobotInGridTests()
{
int p = this.GetNumberOfUniquePaths(3);
Assert.AreEqual(p, 6);
int p1 = this.GetUniquePaths_DP_Memoization_Lazy(3);
Assert.AreEqual(p, p1);
var p2 = this.GetUniquePaths(3);
Assert.AreEqual(p1, p2.Length);
foreach (var path in p2)
{
Debug.WriteLine("===================================================================");
foreach (Tuple<int, int> t in path)
{
Debug.Write(string.Format("({0}, {1}), ", t.Item1, t.Item2));
}
}
p = this.GetNumberOfUniquePaths(4);
Assert.AreEqual(p, 20);
p1 = this.GetUniquePaths_DP_Memoization_Lazy(4);
Assert.AreEqual(p, p1);
p2 = this.GetUniquePaths(4);
Assert.AreEqual(p1, p2.Length);
foreach (var path in p2)
{
Debug.WriteLine("===================================================================");
foreach (Tuple<int, int> t in path)
{
Debug.Write(string.Format("({0}, {1}), ", t.Item1, t.Item2));
}
}
}
答案 3 :(得分:0)
将有无穷无尽的方法。这是因为你可以形成一个无限的位置循环,从而形成无限的可能性。例如:-您可以从 (0,0) 移动到 (0,1),然后移动到 (1,1),然后是 (1,0),然后又回到 (0,0)。这形成了一个位置循环,因此任何人都可以在这些类型的循环中绕来绕去,并拥有无限的可能性。