【问题标题】:Finding all possible paths寻找所有可能的路径
【发布时间】:2016-12-31 14:25:34
【问题描述】:

我很难找到所有可能的路径。

a a a b

b a a a

a b b a

从 0,0 的起点到 2,3 的终点。 我需要获得所有可能的路径。

我可以做的可能动作是向下移动向右移动

让我告诉你我被困在哪里。 我正在尝试使用递归函数。从 0,0 点开始,尽可能向右移动,只有在必须时才向下移动。

我的递归函数:

public static move(int i,int j)
{
     if(possible(x,y+1))
    {
       move(x,y+1);
       move(x+1,y);
    }

}


public static bool possible(int i,int j)
        {
            if((i >=0 && i<3 ) && (j>=0 && j<4))
                return true;
            else
                return false;

        }

不确定我的递归移动功能。还需要扩展它。我不知道我应该如何实施。

我可以使用该移动方法遍历到角节点,但是只要到达角右上角点(0,4)的所有可能移动,我就需要该函数回溯。

【问题讨论】:

  • 您可能会考虑发布实际代码,因为您的移动参数与方法的主体不匹配。我现在看到的是,您的 move 方法首先向下移动,但您的 cmets 表示希望做相反的事情。
  • 我不确定您为什么需要所有可能的路径,最好只采用最短路径,例如使用 Dijkstra 算法。在更大的问题上找到所有路径将需要很长时间。然后再往上走,走最短的路。
  • 增加 'y' 坐标意味着你在右侧行驶,我猜。
  • 我使用 djkstra 对最短路径做了类似的问题。我现在需要所有可能的路径来解决这个问题。
  • 用CanMoveRight和CanMoveDown两种方法替换“可能”方法。

标签: c# recursion traversal


【解决方案1】:

你需要停下来,后退一大步。

第一步应该是提出方法的签名。问题陈述是什么?

找到所有可能的路径

未提及:从特定坐标开始。

所以该方法需要返回一个路径集

static Set<Path> AllPaths(Coordinate start) { /* a miracle happens */ }

好的,现在我们到了某个地方;现在很清楚我们需要什么。我们需要一组东西,我们需要一条路径,我们需要坐标。

什么是坐标?一对整数:

struct Coordinate
{
  public int X { get; }
  public int Y { get; }
  public Coordinate(int x, int y) : this() 
  {
    this.X = x;
    this.Y = y;
  }
}

完成。所以弹出堆栈;什么是路径?路径可以是空的,也可以是第一步后跟路径:

sealed class Path 
{
  private Path() { }
  private Path(Coordinate first, Path rest)
  {
    this.first = first;
    this.rest = rest;
  }
  public static readonly Path Empty = new Path();
  private Coordinate first;
  private Path rest;
  public bool IsEmpty => this == Empty;
  public Coordinate First 
  { 
    get  
    {
      if (this.IsEmpty) throw new Exception("empty!");
      return first;
    }
  }
  public Path Rest
  {   
    get 
    {
      if (this.IsEmpty) throw new Exception("empty!");
      return rest;
    }
  }
  public Path Append(Coordinate c) => new Path(c, this);
  public IEnumerable<Coordinate> Coordinates()
  {
    var current = this;
    while(!current.IsEmpty)
    {
      yield return current;
      current = current.Rest;
    }
  }
}

完成。

现在你实现Set&lt;T&gt;。您将需要进行“所有项目”和“将此集合与另一个组合以产生第三个”的操作。确保集合是不可变的。当您向其中添加新项目时,您不想更改集合;你想要一个不同的集合。加1时不会将3变为4的方法相同; 3和4是不同的数字。

现在您拥有实际解决问题所需的所有工具;现在你可以实现了

static Set<Path> AllPaths(Coordinate start) 
{ 
   /* a miracle happens */ 
}

那么这是如何工作的呢?请记住,所有递归函数都具有相同的形式:

  • 解决小问题
  • 如果我们的情况不是很简单,请将问题简化为更小的情况,递归求解,然后组合解决方案。

那么微不足道的情况是什么?

static Set<Path> AllPaths(Coordinate start) 
{ 
   /* Trivial case: if the start coordinate is at the end already
      then the set consists of one empty path.  */

实现它。

什么是递归案例?

   /* Recursive case: if we're not at the end then either we can go
      right, go down, or both.  Solve the problem recursively for
      right and / or down, union the results together, and add the 
      current coordinate to the top of each path, and return the
      resulting set. */

实现它。

这里的教训是:

  • 列出问题中的所有名词:集合、路径、坐标等。
  • 创建一个代表每一个的类型。保持简单,并确保准确实现每种类型所需的操作。
  • 现在您已经为每个名词实现了一个抽象,您可以开始设计使用这些抽象的算法,并确信它们会起作用。
  • 记住递归的基本规则:如果可以,请解决基本情况;如果不是,请解决较小的递归案例并组合解决方案。

【讨论】:

  • 终于解决了这个问题。尽管我没有阅读您的全部答案。因为我想自己做这一切。但我确实注意到了“堆栈”这个词,谢天谢地,它救了我。 :) 非常感谢。
【解决方案2】:
public void MoveUp(Object sender, MoveEventArgs e)
    {
        if (CanMoveUp(e.CurrentPosition.Y)) ...
    }
    public void MoveDown(Object sender, MoveEventArgs e)
    {
        if (CanMoveDown(e.CurrentPosition.Y)) ...
    }
    public void MoveLeft(Object sender, MoveEventArgs e)
    {
        if (CanMoveLeft(e.CurrentPosition.X)) ...
    }
    public void MoveRight(Object sender, MoveEventArgs e)
    {
        if (CanMoveRight(e.CurrentPosition.X)) ...
    }
    private bool CanMoveUp(double y) => (y - 1) > 0;
    private bool CanMoveDown(double y) => (y + 1) < 4;
    private bool CanMoveLeft(double x) => (x - 1) > 0;
    private bool CanMoveRight(double x) => (x + 1) < 4;

这些值可能不正确,但如果您想要添加任何其他可能的移动障碍,您可以轻松地将添加添加到每个方法中,代码是可重用且易于维护的。

【讨论】:

  • “可重用且易于维护”在这里是有问题的,您已经硬编码了数组/矩阵的两个长度
  • 我同意 Mathias 的观点,但是,这些最好是常量或绑定到他正在工作的任何数据源。我不知道他的程序设计的其余部分,这不是他的问题。
  • 我假设这是一个 2D 地图或游戏,因为我过去曾使用类似的矩阵来进行这样的 2D 遍历地形对象数组,但我还是不知道上下文。跨度>
  • 这显然是一个学生项目...可重用和易于维护并不密切。
【解决方案3】:

很难不放弃农场并提供帮助。您应该将决策逻辑分解为 3 个边界函数

inBoundsX x 
   // return true if x is in bounds, false otherwise

inBoundsY y
   // return true if y is in bounds, false otherwise

inBoundsXY x,y
  // return true if x and y are in bounds, false otherwise

您的递归函数应该始终验证它给出的初始状态,然后决定下一步移动的方式。

move x,y
   if inBoundsXY x,y 
      print I am here x,y 
      // use InboundsX, InboundsY to decide next move.

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-05-06
    • 2012-11-26
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多