跟踪已经完成的递归调用 - C#

时间:2015-11-15 14:06:43

标签: c# recursion

我正在解决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)的路径数相同。

3 个答案:

答案 0 :(得分:3)

你的递归解决方案没问题。

另一个递归解决方案也没问题,它是在动态编程中实现的递归选项'方式。

还有迭代和数学方法。

您可以看到更多here。它来自官方网站。

答案 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;
        }
    }
相关问题