我正在解决this problem,其中我被问到20 x 20网格中从一个角落到另一个角落的最短路径的数量。我知道这是一个简单的组合问题,但我面临的挑战是实现一种解决它的方法。
我的想法是使用递归:网格点(m,n)的路径数等于(m-1,n)的路径数加上(m,n-1)的路径数。我开始时使用以下代码:
using System;
class Program
{
static void Main()
{
long noOfPaths = CountPaths(15, 15);
Console.WriteLine(noOfPaths);
Console.ReadKey();
}
static long CountPaths(int m, int n)
{
if (m == 0) return 1;
else if (n == 0) return 1;
return CountPaths(m-1,n) + CountPaths(m,n-1);
}
}
这非常有效,并返回正确数量的路径,但随着网格大小的增加,其运行时间会急剧增加,而我无法达到20x20。上面的一个主要问题是它不止一次在同一个网格点上进行递归调用,我想要一些关于跟踪这个问题的最佳方法的建议。到目前为止,我已经在"全球变量"上找到了帖子。围绕这个网站,我的解决方案是创建一个可以从任何地方访问的数组。下面的代码解决了我的问题,也很快。
using System;
namespace problem15
{
class Program
{
static void Main()
{
long noOfPaths = CountPaths(Values.m-1, Values.n-1);
Console.WriteLine(noOfPaths);
Console.ReadKey();
}
static long CountPaths(int m, int n)
{
if (m == 0) return 1;
else if (n == 0) return 1;
if (Values.A[m - 1, n] == 0) Values.A[m - 1, n] = CountPaths(m - 1, n);
if (Values.A[m, n - 1] == 0) Values.A[m, n - 1] = CountPaths(m, n - 1);
return Values.A[m-1,n] + Values.A[m,n-1];
}
}
static class Values
{
static public int m = 21, n = 21;
static public long[,] A = new long[m, n];
}
}
这是问题的正确解决方案,还是被认为是错误的形式"?另外,我知道在这个问题上有更多的优化,例如到(k,l)的路径数与到(l,k)的路径数相同。
答案 0 :(得分:3)
答案 1 :(得分:2)
你的第二个解决方案中的想法是好的,这是一个众所周知的技术,称为Memoization。然而,实施不是。使用共享(“全局”)状态极大地限制了方法的使用,不计算您编写方法的事实,它只能被调用一次并且参数是硬编码的。这是正确的方法。
让我们从第一个解决方案开始,将它封装在一个类中,并将非递归与递归部分分开:
public class MyAlgorithms
{
public static long CountPaths(int m, int n)
{
// Agrument validations goes here
return CountPathsRecursive(m, n);
}
private static long CountPathsRecursive(int m, int n)
{
if (m == 0 || n == 0) return 1;
var count = CountPathsRecursive(m - 1, n) + CountPathsRecursive(m, n - 1);
return count;
}
}
并使用它
using System;
class Program
{
static void Main()
{
long noOfPaths = MyAlgorithms.CountPaths(21, 21);
Console.WriteLine(noOfPaths);
Console.ReadKey();
}
}
现在,您可以通过应用第二个想法而不会影响使用情况来优化实施
public class MyAlgorithms
{
public static long CountPaths(int m, int n)
{
// Agrument validations goes here
var counts = new long[m, n];
return CountPathsRecursive(m, n, counts);
}
private static long CountPathsRecursive(int m, int n, long[,] counts)
{
if (m == 0 || n == 0) return 1;
var count = counts[m - 1, n - 1];
if (count == 0) counts[m - 1, n - 1] = count =
CountPathsRecursive(m - 1, n, counts) + CountPathsRecursive(m, n - 1, counts);
return count;
}
}
希望你能得到这个想法。同样的方法你可以改变实现使用迭代算法,只需公式等。
答案 2 :(得分:1)
只是为了获得纯粹的OO解决方案(既不快速也不非贪婪;-)):
有一个优点:您可以使用完整的单元格列表找到所有找到的路径。
public class MyGrid {
public int Width { get; protected set; }
public int Height { get; protected set; }
public MyCell[,] MyCells { get; protected set; }
public List<MyPath> MyPathList;
public MyGrid(int h, int w) {
this.Width = w;
this.Height = h;
this.MyCells = new MyCell[this.Width, this.Height];
for (int x = 0; x < Width; x++) {
for (int y = 0; y < Width; y++) {
this.MyCells[x, y] = new MyCell(this, x, y);
}
}
this.MyPathList = new List<MyPath>();
}
public int FindPaths() {
this.MyPathList.Clear();
var p = new MyPath(this);
this.MyPathList.Add(p);
var c = new MyCell(this,0,0);
p.AddCellRecursive(c);
return MyPathList.Count;
}
}
public class MyCell {
public MyGrid myGrid { get; protected set; }
public int X { get; protected set; }
public int Y { get; protected set; }
public MyCell(MyGrid gr, int x, int y) {
this.myGrid = gr;
this.X = x;
this.Y = y;
}
public MyCell RightNeighbour {
get {
if (this.X == this.myGrid.Width-1)
return null;
else
return this.myGrid.MyCells[this.X+1, this.Y];
}
}
public MyCell BelowNeighbour {
get {
if (this.Y == this.myGrid.Height-1)
return null;
else
return this.myGrid.MyCells[this.X, this.Y+1];
}
}
public override string ToString() {
return string.Format("{0}|{1}", this.X, this.Y);
}
}
public class MyPath{
public MyGrid myGrid { get; protected set; }
public List<MyCell> MyCellList;
public MyPath(MyGrid gr) {
this.myGrid = gr;
this.MyCellList = new List<MyCell>(); }
public void AddCellRecursive(MyCell c) {
this.MyCellList.Add(c);
var r = c.RightNeighbour;
var b = c.BelowNeighbour;
MyPath second=null;
if (b == null && r == null)
return;//end
else if (r == null) {
second = this;
}
else {
second = this.Clone();
this.myGrid.MyPathList.Add(second);
this.AddCellRecursive(r);
}
if (b == null)
this.myGrid.MyPathList.Remove(second);
else
second.AddCellRecursive(b);
}
public MyPath Clone(){
var retPath = new MyPath(this.myGrid);
foreach (var c in MyCellList) {
retPath.MyCellList.Add(c);
}
return retPath;
}
}