Knight's Shortest Path on Chessboard

后端 未结 16 1283
情深已故
情深已故 2020-11-30 16:42

I\'ve been practicing for an upcoming programming competition and I have stumbled across a question that I am just completely bewildered at. However, I feel as though it\'s

相关标签:
16条回答
  • 2020-11-30 16:54

    EDIT: See simon's answer, where he fixed the formula presented here.

    Actually there is an O(1) formula

    This is an image that I've made to visualize it ( Squares a knight can reach on Nth move are painted with same color ). Knight's Move

    Can you notice the pattern here?

    Although we can see the pattern, it is really hard to find the function f( x , y ) that returns the number of moves required to go from square ( 0 , 0 ) to square ( x , y )

    But here is the formula that works when 0 <= y <= x

    int f( int x , int y )
    {
        int delta = x - y;
    
        if( y > delta )
            return 2 * ( ( y - delta ) / 3 ) + delta;
        else
            return delta - 2 * ( ( delta - y ) / 4 );
    }
    

    Note: This question was asked on SACO 2007 Day 1
    And solutions are here

    0 讨论(0)
  • 2020-11-30 16:54

    I think that this might also help you..

    NumWays(x,y)=1+min(NumWays(x+-2,y-+1),NumWays(x+-1,y+-2)); 
    

    and using Dynamic Programming to get the solution.

    P.S: It kinda uses the BFS without having to take the trouble of declaring the nodes and edges of the graph.

    0 讨论(0)
  • 2020-11-30 17:00

    here's the PHP version of Jules May's function

    function knightDistance($x, $y)
    {
        $x = abs($x);
        $y = abs($y);
    
        if($x < $y)
        {
            $tmp = $x;
            $x = $y;
            $y = $tmp;
        }
    
        if($x > 2 * $y)
        {
            $n7 = 0;
            $n8 = floor(($x + 2*$y) / 4);
            $n10 = floor(($x - 2*$y +1) / 4);
        }
        else
        {
            $n7 = floor((2*$y - $x) / 3);
            $n8 = floor((2*$x - $y) / 3);
            $n10 = 0;
        }
    
        $x -= 2 * $n8 + $n7 + 2 * $n10;
        $y -= $n8 + 2 * $n7 - $n10;
    
        if($x == 1 && $y == 0)
        {
            if($n8 > 0)
            {
                $x = 3;
                $y = 1;
                $n8--;
            }
        }
        if($x == 2 && $y == 2)
        {
            if($n8 > 0)
            {
                $x = 3;
                $y = 1;
                $n8--;
                $n7++;
            }
        }
    
        $cheatsheet = [[0, 3, 2], [2, 0, 2], [4]];
    
        return $n7 + $n8 + $n10 + $cheatsheet [$y][$x-$y];
    }
    
    0 讨论(0)
  • 2020-11-30 17:01
    /*
    This program takes two sets of cordinates on a 8*8 chessboard, representing the
    starting and ending points of a knight's path.
    The problem is to print the cordinates that the knight traverses in between, following
    the shortest path it can take.
    Normally this program is to be implemented using the Djikstra's algorithm(using graphs)
    but can also be implemented using the array method.
    NOTE:Between 2 points there may be more than one shortest path. This program prints
    only one of them.
    */
    
    #include<stdio.h>
    
    #include<stdlib.h>
    
    #include<conio.h>
    
    int m1=0,m2=0;
    
    /*
    This array contains three columns and 37 rows:
    The rows signify the possible coordinate differences.
    The columns 1 and 2 contains the possible permutations of the row and column difference 
    between two positions on a chess board;
    The column 3 contains the minimum number of steps involved in traversing the knight's 
    path with the given permutation*/
    
    int arr[37][3]={{0,0,0},{0,1,3},{0,2,2},{0,3,3},{0,4,2},{0,5,3},{0,6,4},{0,7,5},    {1,1,2},{1,2,1},{1,3,2},{1,4,3},{1,5,4},{1,6,3},{1,7,4},{2,2,4},{2,3,3},{2,4,2},
                {2,5,3},{2,6,3},{2,7,5},{3,3,2},{3,4,3},{3,5,4},{3,6,3},{3,7,4},{4,4,4},{4,5,3},{4,6,4},{4,7,5},{5,5,4},{5,6,5},{5,7,4},{6,6,5},{6,7,5},{7,7,6}};
    
    void printMoves(int,int,int,int,int,int);
    void futrLegalMove(int,int,int,int);
    main()
    {
      printf("KNIGHT'S SHORTEST PATH ON A 8*8 CHESSBOARD :\n");
      printf("------------------------------------------");
      printf("\nThe chessboard may be treated as a 8*8 array here i.e. the (1,1) ");
      printf("\non chessboard is to be referred as (0,0) here and same for (8,8) ");
      printf("\nwhich is to be referred as (7,7) and likewise.\n");
      int ix,iy,fx,fy;
      printf("\nEnter the initial position of the knight :\n");
      scanf("%d%d",&ix,&iy);
      printf("\nEnter the final position to be reached :\n");
      scanf("%d%d",&fx,&fy);
      int px=ix,py=iy;
      int temp;
      int tx,ty;
      printf("\nThe Knight's shortest path is given by :\n\n");
      printf("(%d, %d)",ix,iy);
      futrLegalMove(px,py,m1,m2);
      printMoves(px,py,fx,fy,m1,m2);
       getch();
    } 
    
    /*
      This method checkSteps() checks the minimum number of steps involved from current
      position(a & b) to final position(c & d) by looking up in the array arr[][].
    */
    
    int checkSteps(int a,int b,int c,int d)
    {  
        int xdiff, ydiff;
        int i, j;
        if(c>a)
            xdiff=c-a;
        else
            xdiff=a-c;
        if(d>b)
            ydiff=d-b;
        else
            ydiff=b-d;
        for(i=0;i<37;i++)
            {
                if(((xdiff==arr[i][0])&&(ydiff==arr[i][1])) || ((xdiff==arr[i][1])&& (ydiff==arr[i] [0])))
                {
                    j=arr[i][2];break;
                }
            }
    
            return j;
    }   
    
    /*
    This method printMoves() prints all the moves involved.
    */
    
    void printMoves(int px,int py, int fx, int fy,int a,int b)
    {    
     int temp;
     int tx,ty;
     int t1,t2;
      while(!((px==fx) && (py==fy)))
      {   
          printf(" --> ");
          temp=checkSteps(px+a,py+b,fx,fy);
          tx=px+a;
          ty=py+b;
          if(!(a==2 && b==1))
          {if((checkSteps(px+2,py+1,fx,fy)<temp) && checkMove(px+2,py+1))
          {temp=checkSteps(px+2,py+1,fx,fy);
           tx=px+2;ty=py+1;}}
          if(!(a==2 && b==-1))
          {if((checkSteps(px+2,py-1,fx,fy)<temp) && checkMove(px+2,py-1))
          {temp=checkSteps(px+2,py-1,fx,fy);
           tx=px+2;ty=py-1;}}
          if(!(a==-2 && b==1))
          {if((checkSteps(px-2,py+1,fx,fy)<temp) && checkMove(px-2,py+1))
          {temp=checkSteps(px-2,py+1,fx,fy);
           tx=px-2;ty=py+1;}}
          if(!(a==-2 && b==-1))
          {if((checkSteps(px-2,py-1,fx,fy)<temp) && checkMove(px-2,py-1))
          {temp=checkSteps(px-2,py-1,fx,fy);
           tx=px-2;ty=py-1;}}
          if(!(a==1 && b==2))
          {if((checkSteps(px+1,py+2,fx,fy)<temp) && checkMove(px+1,py+2))
          {temp=checkSteps(px+1,py+2,fx,fy);
           tx=px+1;ty=py+2;}}
          if(!(a==1 && b==-2))
          {if((checkSteps(px+1,py-2,fx,fy)<temp) && checkMove(px+1,py-2))
          {temp=checkSteps(px+1,py-2,fx,fy);
           tx=px+1;ty=py-2;}}
          if(!(a==-1 && b==2))
          {if((checkSteps(px-1,py+2,fx,fy)<temp) && checkMove(px-1,py+2))
          {temp=checkSteps(px-1,py+2,fx,fy);
           tx=px-1;ty=py+2;}}
          if(!(a==-1 && b==-2))
          {if((checkSteps(px-1,py-2,fx,fy)<temp) && checkMove(px-1,py-2))
          {temp=checkSteps(px-1,py-2,fx,fy);
           tx=px-1;ty=py-2;}}
           t1=tx-px;//the step taken in the current move in the x direction.
           t2=ty-py;//" " " " " " " " " " " " " " " " " " " " " y " " " " ".
           px=tx;
           py=ty;
           printf("(%d, %d)",px,py);
           futrLegalMove(px,py,t1,t2);
           a=m1;
           b=m2;
       }
    
    } 
    
    /*
    The method checkMove() checks whether the move in consideration is beyond the scope of
    board or not.
    */   
    
    int checkMove(int a, int b)
    {
        if(a>7 || b>7 || a<0 || b<0)
            return 0;
        else
            return 1;
    }
    
    /*Out of the 8 possible moves, this function futrLegalMove() sets the valid move by
      applying the following constraints
          1. The next move should not be beyond the scope of the board.
          2. The next move should not be the exact opposite of the previous move.
      The 1st constraint is checked by sending all possible moves to the checkMove() 
      method;
      The 2nd constraint is checked by passing as parameters(i.e. a and b) the steps of the 
      previous move and checking whether or not it is the exact opposite of the current move.
    */
    
    void futrLegalMove(int px,int py,int a,int b)
    {
         if(checkMove(px+2,py+1) && (a!=-2 && b!=-1))
             m1=2,m2=1;
         else
         {
             if(checkMove(px+2,py-1)&& (a!=-2 && b!=1))
                 m1=2,m2=-1;
         else
         {
             if(checkMove(px-2,py+1)&& (a!=2 && b!=-1))
                  m1=-2,m2=1;
         else
         {
             if(checkMove(px-2,py-1)&& (a!=2 && b!=1))
                   m1=-2,m2=-1;
         else
         {
             if(checkMove(px+1,py+2)&& (b!=-2 && a!=-1))
                   m2=2,m1=1;
         else
         {
             if(checkMove(px+1,py-2)&& (a!=-1 && b!=2))
                   m2=-2,m1=1;
         else
         {
             if(checkMove(px-1,py+2)&& (a!=1 && b!=-2))
                   m2=2,m1=-1;
         else
         {
             if(checkMove(px-1,py-2)&& (a!=1 && b!=2))
                   m2=-2,m1=-1;
         }}}}}}}
    }
    
    //End of Program.
    

    I haven't studied graphs yet..as per the problem of implementing it through simply arrays, I could not derive any solution other than this. I treated the positions not as ranks and files(The usual chess notation), but as array indices. FYI, this is for a 8*8 chessboard only. Any improvement advice is always welcomed.

    *The comments should suffice for your understanding of the logic. However, you may always ask.

    *Checked on DEV-C++ 4.9.9.2 compiler(Bloodshed Software).

    0 讨论(0)
  • 2020-11-30 17:04

    Here is a C version based on Mustafa Serdar Şanlı code that works for a finit board:

    #include <stdio.h>
    #include <math.h>
    
    #define test(x1, y1, x2, y2) (sx == x1 && sy == y1 &&tx == x2 &&ty == y2) || (sx == x2 && sy == y2 && tx == x1 && ty==y1)
    
    int distance(int sx, int sy, int tx, int ty) {
        int x, y, t;
        double delta;
    
        // special corner cases 
        if (test(1, 1, 2, 2) || 
            test(7, 7, 8, 8) || 
            test(7, 2, 8, 1) || 
            test(1, 8, 2, 7))
            return 4;
    
        // axes symmetry 
        x = abs(sx - tx);
        y = abs(sy - ty);
    
        // diagonal symmetry 
        if (x < y) {
            t = x;
            x = y;
            y = t;
        }
    
        // 2 corner cases
        if (x == 1 && y == 0)
            return 3;
        if (x == 2 && y == 2)
            return 4;
    
        // main
        delta = x - y;
        if (y > delta) {
            return (int)(delta - 2 * floor((delta - y) / 3));
        }
        else {
            return (int)(delta - 2 * floor((delta - y) / 4));
        }
    }
    

    Test it here with proof against a recursive solution

    0 讨论(0)
  • 2020-11-30 17:05

    You have a graph here, where all available moves are connected (value=1), and unavailable moves are disconnected (value=0), the sparse matrix would be like:

    (a1,b3)=1,
    (a1,c2)=1,
      .....
    

    And the shortest path of two points in a graph can be found using http://en.wikipedia.org/wiki/Dijkstra's_algorithm

    Pseudo-code from wikipedia-page:

    function Dijkstra(Graph, source):
       for each vertex v in Graph:           // Initializations
           dist[v] := infinity               // Unknown distance function from source to v
           previous[v] := undefined          // Previous node in optimal path from source
       dist[source] := 0                     // Distance from source to source
       Q := the set of all nodes in Graph
       // All nodes in the graph are unoptimized - thus are in Q
       while Q is not empty:                 // The main loop
           u := vertex in Q with smallest dist[]
           if dist[u] = infinity:
              break                         // all remaining vertices are inaccessible from source
           remove u from Q
           for each neighbor v of u:         // where v has not yet been removed from Q.
               alt := dist[u] + dist_between(u, v) 
               if alt < dist[v]:             // Relax (u,v,a)
                   dist[v] := alt
                   previous[v] := u
       return dist[]
    

    EDIT:

    1. as moron, said using the http://en.wikipedia.org/wiki/A*_algorithm can be faster.
    2. the fastest way, is to pre-calculate all the distances and save it to a 8x8 full matrix. well, I would call that cheating, and works only because the problem is small. But sometimes competitions will check how fast your program runs.
    3. The main point is that if you are preparing for a programming competition, you must know common algorithms including Dijkstra's. A good starting point is reading Introduction to Algorithms ISBN 0-262-03384-4. Or you could try wikipedia, http://en.wikipedia.org/wiki/List_of_algorithms
    0 讨论(0)
提交回复
热议问题