2011-05-05 110 views
5

考虑到5x5网格,它需要记录所有可能的骑士移动组合,从每个单独的起始方块开始,经过每个后续方块。递归树搜索

我设想它是一个像结构一样的二叉树,但是因为棋盘上的每个方块都可能有超过2个潜在的下一步棋,所以我认为它不会奏效。我研究了A */Pathfinding算法,但是他们都需要一个终端节点来处理我所能看到的内容,而且我不知道终端节点,因为它每次都在进行,并且会有所不同。

我的伪迄今是:

For each square on the board 
    Check if this key has potential moves 
     If Potential moves 
      <Some way of selecting a next move (this could be the square we just originated from too!)> 
      Register this move into a collection we can check against for subsequent        moves 
      Recursively call function with the square we just landed on 
     Else Continue 
End 

任何意见/指针将不胜感激,因为我很失落!

+0

您可能希望查看LinkedList数据结构。你有没有与你的教授核对,以确认你是否在正确的轨道与偷窥代码碰巧?因为我之前从未做过这样的项目,究竟是什么使移动成为有效的,只是连接到正方形的网格上的正方形是正确的? – 2011-05-05 11:56:08

+0

您是否被要求列出所有旅行团,或者统计到达每个广场的方式数量,或者究竟是什么?如果你被要求列出所有的旅行团,那么你列出他们的顺序是否有关系? – 2011-05-05 12:01:33

回答

3

好的,会有极端数量的可能的动作序列,正如评论所暗示的那样。 这是从我的头顶,我是如此裸露....

非递归版本:你需要的位置列表的列表(称为位置列表),这将是你最后的答案,我会将该列表称为路由列表。

为每个起始位置创建一个列表,并将它们全部放入路由列表中。

While the routes list has a position list that's less than the required length 
{ 
    Get a position list that's too short. 
    Remove it from the routes list 
    Create new position lists that are a copy of the list we just removed + a possible next position from the last position in the list. 
    Add those lists to the routes list. 
} 

编辑:递归版本:

using System.Collections.Generic; 
using System.Drawing; 
using System.Linq; 

namespace ConsoleApplication1 
{ 
    class Program 
    { 
     static int GridSize = 5; 

     static void Main(string[] args) 
     { 
      List<List<Point>> Positions = (from X in Enumerable.Range(0, GridSize) 
              from Y in Enumerable.Range(0, GridSize) 
              select new List<Point>() { new Point(X, Y) }).ToList(); 

      var PossibleRoutes = WalkGraph(Positions, 5); 
     } 

     static List<List<Point>> WalkGraph(List<List<Point>> RoutesList, int DesiredLength) 
     { 
      List<List<Point>> Result = new List<List<Point>>(); 

      foreach (var Route in RoutesList) 
      { 
       if (Route.Count < DesiredLength) 
       { 
        // Extend the route (produces a list of routes) and recurse 
        Result.AddRange(WalkGraph(ExtendRoute(Route), DesiredLength)); 
       } 
       else 
       { 
        Result.Add(Route); 
       } 
      } 

      return Result; 
     } 

     static List<List<Point>> ExtendRoute(List<Point> Route) 
     { 
      List<List<Point>> NextMoveRoutes = new List<List<Point>>(); 

      // Itterate through each possible move 
      foreach (var NextMove in PossibleMoves(Route.Last())) 
      { 
       // Create a copy of the route, and add this possible move to it. 
       List<Point> NextMoveRoot = new List<Point>(Route); 
       NextMoveRoot.Add(NextMove); 
       NextMoveRoutes.Add(NextMoveRoot); 
      } 

      return NextMoveRoutes; 
     } 

     static List<Point> PossibleMoves(Point P) 
     { 
      // TODO Generate a list of possible places to move to 

      List<Point> Result = new List<Point>(); 

      Result.Add(new Point(P.X + 1, P.Y + 2)); 
      Result.Add(new Point(P.X - 1, P.Y + 2)); 
      Result.Add(new Point(P.X + 1, P.Y - 2)); 
      Result.Add(new Point(P.X - 1, P.Y - 2)); 

      Result.Add(new Point(P.X + 2, P.Y + 1)); 
      Result.Add(new Point(P.X - 2, P.Y + 1)); 
      Result.Add(new Point(P.X + 2, P.Y - 1)); 
      Result.Add(new Point(P.X - 2, P.Y - 1)); 

      Result.RemoveAll(PossibleMove => PossibleMove.X < 0 || PossibleMove.X > GridSize || 
              PossibleMove.Y < 0 || PossibleMove.Y > GridSize); 

      return Result; 
     } 
    } 
} 

编辑:下面是一个使用IEnumerable的,以消除最初的计算时间,并大幅降低内存占用一个版本。

using System; 
using System.Collections.Generic; 
using System.Drawing; 
using System.Linq; 

namespace ConsoleApplication1 
{ 
    class Program 
    { 
     static int GridSize = 5; 

     static void Main(string[] args) 
     { 
      IEnumerable<IEnumerable<Point>> Positions = from X in Enumerable.Range(0, GridSize) 
                 from Y in Enumerable.Range(0, GridSize) 
                 select new List<Point>() { new Point(X, Y) } as IEnumerable<Point>; 

      var PossibleRoutes = WalkGraph(Positions, 100); 

      foreach (var Route in PossibleRoutes) 
      { 
       Console.WriteLine(Route.Select(P => P.ToString()).Aggregate((curr, next) => curr + " " + next)); 
       //Thread.Sleep(500); // Uncomment this to slow things down so you can read them! 
      } 

      Console.ReadKey(); 
     } 

     static IEnumerable<IEnumerable<Point>> WalkGraph(IEnumerable<IEnumerable<Point>> RoutesList, int DesiredLength) 
     { 
      foreach (var Route in RoutesList) 
      { 
       if (Route.Count() < DesiredLength) 
       { 
        // Extend the route (produces a list of routes) and recurse 
        foreach (var ExtendedRoute in WalkGraph(ExtendRoute(Route), DesiredLength)) 
         yield return ExtendedRoute; 
       } 
       else 
       { 
        //Result.Add(Route); 
        yield return Route; 
       } 
      } 
     } 

     static IEnumerable<IEnumerable<Point>> ExtendRoute(IEnumerable<Point> Route) 
     { 
      // Itterate through each possible move 
      foreach (var NextMove in PossibleMoves(Route.Last())) 
      { 
       // Create a copy of the route, and add this possible move to it. 
       List<Point> NextMoveRoute = new List<Point>(Route); 
       NextMoveRoute.Add(NextMove); 
       yield return NextMoveRoute; 
      } 
     } 

     static IEnumerable<Point> PossibleMoves(Point P) 
     { 
      List<Point> Result = new List<Point>(); 

      Result.Add(new Point(P.X + 1, P.Y + 2)); 
      Result.Add(new Point(P.X - 1, P.Y + 2)); 
      Result.Add(new Point(P.X + 1, P.Y - 2)); 
      Result.Add(new Point(P.X - 1, P.Y - 2)); 

      Result.Add(new Point(P.X + 2, P.Y + 1)); 
      Result.Add(new Point(P.X - 2, P.Y + 1)); 
      Result.Add(new Point(P.X + 2, P.Y - 1)); 
      Result.Add(new Point(P.X - 2, P.Y - 1)); 

      Result.RemoveAll(PossibleMove => PossibleMove.X < 0 || PossibleMove.X > GridSize || 
              PossibleMove.Y < 0 || PossibleMove.Y > GridSize); 

      return Result as IEnumerable<Point>; 
     } 
    } 
} 
+0

在我看到您编辑的文章之前发表了评论,让我看看它,然后我会重新发表评论 – Gary 2011-05-05 13:06:03

+0

非常感谢您,这种迭代方式确实可行(尽管一旦您达到一定数量的序列,但出于显而易见的原因 - 有一吨的递归正在进行)。 – Gary 2011-05-05 14:04:24

+0

@加里,我编辑了我的答案,包括使用IEnumerable的版本。根据您的使用情况,它可能会删除内存使用问题。 – 2011-05-05 14:19:54

1

虽然深度优先搜索会的工作,我认为你可以做到这一点更好(快)与广度优先搜索,因为你实际上并不需要生成动作,你只需要数数的举动。

如果您可以在第(n-1)次移动时创建包含可能分支数的矩阵,则可以使用该矩阵计算第n次移动可能分支的计数。

在迭代0,矩阵仅仅是那些矩阵,因为你没有移动你的作品尚未:

table0 

    1 1 1 1 
    1 1 1 1 
    1 1 1 1 
    1 1 1 1 

让我们命名上面table0表,因为这是第0举动。要从table0获得table1,您需要table1[r1c1] = table0[r2c3] + table0[r3c2],因为r1c1只能由r2c3和r3c2中的骑士达成,另一个示例可以找到table1[r2c2] = table0[r1c4] + table0[r3c4] + table0[r4c3] + table0[r4c1],因为r2c2只能由r1c4,r3c4,r4c3,r4c1中的骑士抵达。等等。

使用这种算法,该表的未来几年的值是这样:

table1 

    2 3 3 2 
    3 4 4 3 
    3 4 4 3 
    2 3 3 2 




    table2 

    8 10 10 8 
    10 10 10 10 
    10 10 10 10 
    8 10 10 8 




    table3 

    20 30 30 20 
    30 36 36 30 
    30 36 36 30 
    20 30 30 20 




    table4 

    72 96 96 72 
    96 100 100 96 
    96 100 100 96 
    72 96 96 72 



    table5 

    200 292 192 200 
    192 336 336 192 
    192 336 336 192 
    200 192 192 200 

所以在这个4x4的比赛基本上,这将是计算下一格的公式:

last = table from previous iteration 
next = create new table of zeros 

// corners 
next[r1c1] = last[r2c3] + last[r3c2] 
next[r1c4] = last[r2c2] + last[r3c3] 
next[r4c1] = last[r2c3] + last[r3c2] 
next[r4c4] = last[r3c3] + last[r3c3] 

// sides, clockwise 
next[r1c2] = last[r3c1] + last[r3c3] + last[r2c4] 
next[r1c3] = last[r3c2] + last[r3c4] + last[r2c1] 
next[r2c4] = last[r1c2] + last[r3c2] + last[r4c3] 
next[r3c4] = last[r2c2] + last[r4c2] + last[r1c3] 
next[r4c3] = last[r2c2] + last[r2c4] + last[r3c1] 
next[r4c2] = last[r2c1] + last[r2c3] + last[r3c4] 
next[r3c1] = last[r2c3] + last[r4c3] + last[r1c2] 
next[r2c1] = last[r1c3] + last[r3c3] + last[r4c2] 

// middle four 
next[r2c2] = last[r1c4] + last[r3c4] + last[r4c1] + last[r4c3] 
next[r2c3] = last[r1c1] + last[r3c1] + last[r4c2] + last[r4c4] 
next[r3c2] = last[r1c1] + last[r1c3] + last[r2c4] + last[r4c4] 
next[r3c3] = last[r2c1] + last[r4c1] + last[r1c2] + last[r1c4] 

这将需要不断的内存O(1),并且根据移动的次数,操作的数量是线性的O(n)。注意:你需要检查这个算法是否真正起作用,我还没有考虑过它是否正确计算了计数。

+0

我很喜欢这种方法。为了使其适用于任何规模,您可以遍历表格中的每个点,并为该点可能的移动中的每个点递增目标点。我不确定这是OP之后的情况,因为这只会给你在所有路线上遇到的每个位置的次数。我有一个唠叨的感觉,这个想法可以适应解决这个问题,但我不能很好地解决这个问题。 – 2011-05-05 15:20:38

0

所以这可以通过DFS来完成。我相当肯定,自从DFS生成每条路径后,有更快的方法,并且有O(2^{count})个路径。这里是刚刚DFS从每一个出发点的每一条路径一些Python代码

def extended_knight_tours(width, height, count): 
    start_x, start_y = -1, -1 

    def dfs(x, y, moves_left): 
     if not (0 <= x < width and 0 <= y < height): 
      return 0 
     if moves_left == 0: 
      if (start_x, start_y) == (x, y): 
       return 1 
      else: 
       return 0 

     knight_moves = [(1, 2), (-1, 2), (1, -2), (-1, -2), 
         (2, 1), (2, -1), (-2, 1), (-2, -1)] 

     return sum(dfs(x + dx, y + dy, moves_left - 1) 
        for dx, dy in knight_moves) 

    ans = 0 
    for x in range(width): 
     for y in range(height): 
      start_x = x 
      start_y = y 
      ans += dfs(x, y, count) 

    return ans 

一个简单的空间与时间的权衡,你可以加快这只是memoize的的DFS(记得要清除缓存为每个启动位置)。

从玩这个功能,我注意到,每个奇数的答案是零。因此,一个可能更快的算法是找到每个起始位置的长度计数/ 2(不一定是路由)的路径的数量。然后,可以使用count/2值计算位置为中点的路径数量,但我会将其作为练习留给读者:)