ZOJ-1091-Knight Moves

http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemCode=1091

编程任务:输入两个方格a和b,确定骑士在最短路径上从a到b移动的次数

算法分析:

1、  最容易想到就是深度优先搜索了,从起点向八个方向递归求解,计算起点到各个点的最短路径,但是深搜速度慢,如果棋盘大的话, 很容易就超时了

2、  然后就是广度优先搜索了,从起点进行8个方向的搜索,搜索到终点即结束

3、  觉得这个方法确实比较好,如果让我想估计想不到:棋盘是8*8的,在任意一个方格,骑士都可以走到棋盘上的任意其他方格,这样就有64种可能;因为有64格,所以要保存任意两格之间的最短路径的步数,需要64*64的数组。 int Knight[64][64];

建立64*64的矩阵数组与棋盘之间的对应关系。设矩阵数组的单元(i,j)(0=<i,j<=63),对应棋盘上的骑士从方格a(i/8,i%8)跳到方格b(j/8,j%8),如果只需要走一步则为1,否则为无穷:方格自身为0

设(x,y)为方格a和b之间的坐标差值,计算公式:简化代码,用abs()函数表示

x = i/8 – j/8;               x = abs(i/8 – j/8);

y = i%8 – j%8;            y = abs(i%8 – j%8);

在棋盘上(x,y)的相对位置有8个方向,如图阴影单元格

 

(-1,2)

 

(1,2)

 

(-2,1)

 

 

 

(2,1)

 

 

(0,0)

 

 

(-2,-1)

 

 

 

(2,-1)

 

(-1,-2)

 

(1,-2)

 

可以看到,当x变化为1时y变化为2,或者x变化为2时y变化为1

所以就有: if(x == 1 && y == 2 || x == 2 && y == 1)      Knight[i[j] = Knight[j][i] = 1;

然后采用floyd算法求出所有单元格的最短路径

深搜:

View Code
#include<string.h>

#include<stdio.h>



#define MAXN 9



int dir[8][2] = {-1,-2, -2,-1, -2,1, -1,2, 1,2, 2,1, 2,-1, 1,-2};

int ans[MAXN][MAXN];



void dfs(int si,int sj,int moves)

{

    if(si<1||si>8||sj<1||sj>8||moves>=ans[si][sj])    return ;

    ans[si][sj] = moves;        //最优解

    int i;

    for(i=0;i<8;i++)

        dfs(si+dir[i][0],sj+dir[i][1],moves+1);

}



int main()

{

    char str[6];

    int i,j;

    while(gets(str))

    {

        for(i=0;i<MAXN;i++)

        {

            for(j=0;j<MAXN;j++)

                ans[i][j] = 1000;

        }

        dfs(str[0]-'a'+1,str[1]-'0',0);    

        printf("To get from %c%c to %c%c takes %d knight moves.\n",str[0],str[1],str[3],str[4],ans[str[3]-'a'+1][str[4]-'0']);

    }

    return 0;

}

广搜:

View Code
#include<cstring>

#include<iostream>

#include<queue>

#include<cstdio>

using namespace std;



struct Knight{

    int x,y,moves;

};

int dir[8][2] = {-1,-2, -2,-1, -2,1, -1,2, 1,2, 2,1, 2,-1, 1,-2};



int main()

{

    char str[6];

    int i;

    queue<Knight>Q;

    Knight start,end,from,to;

    while(gets(str))

    {

        while(!Q.empty())    Q.pop();

        start.x = str[0]-'a'+1;

        start.y = str[1]-'0';

        start.moves = 0;

        end.x = str[3]-'a'+1;

        end.y = str[4]-'0';

        Q.push(start);

        while(1)

        {

            from = Q.front();    Q.pop();

            if(from.x == end.x && from.y == end.y) break;

            for(i=0;i<8;i++)

            {

                to.x = from.x + dir[i][0];

                to.y = from.y + dir[i][1];

                if(to.x < 1 || to.x > 8 || to.y < 1 || to.y > 8) continue;

                to.moves = from.moves + 1;

                Q.push(to);

            }

        }

        cout<<"To get from "<<str[0]<<str[1]<<" to "<<str[3]<<str[4]<<" takes "<<from.moves<<" knight moves."<<endl;

    }

    return 0;

}

后来又定义个标记数组来限制方格的重复计算,时间确实有所缩短

View Code
#include<cstring>

#include<iostream>

#include<queue>

#include<cstdio>

using namespace std;



struct Knight{

    int x,y,moves;

};

int dir[8][2] = {-1,-2, -2,-1, -2,1, -1,2, 1,2, 2,1, 2,-1, 1,-2};

int flag[9][9];

int main()

{

    char str[6];

    int i;

    queue<Knight>Q;

    Knight start,end,from,to;

    while(gets(str))

    {

        while(!Q.empty())    Q.pop();

        memset(flag,0,sizeof(flag));

        start.x = str[0]-'a'+1;

        start.y = str[1]-'0';

        start.moves = 0;

        end.x = str[3]-'a'+1;

        end.y = str[4]-'0';

        Q.push(start);

        flag[start.x][start.y] = 1;

        while(1)

        {

            from = Q.front();    Q.pop();

            if(from.x == end.x && from.y == end.y) break;

            for(i=0;i<8;i++)

            {

                to.x = from.x + dir[i][0];

                to.y = from.y + dir[i][1];

                if(to.x < 1 || to.x > 8 || to.y < 1 || to.y > 8) continue;

                if(!flag[to.x][to.y])

                {

                    to.moves = from.moves + 1;                    

                    Q.push(to);

                    flag[to.x][to.y] = 1;

                }

            }

        }

        cout<<"To get from "<<str[0]<<str[1]<<" to "<<str[3]<<str[4]<<" takes "<<from.moves<<" knight moves."<<endl;

    }

    return 0;

}

Floyd算法:

View Code
#include <stdio.h>

#include <string.h>

#include <math.h>



int Knight[64][64];



void work()    //求出棋盘上任意两个格子之间的最短路径

{

    int i,j,k,x,y;

    for(i=0;i<64;i++)

        for(j=0;j<64;j++)

            Knight[i][j] = 30;

    for(i=0;i<64;i++)

    {

        for(j=0;j<64;j++)

        {

            x = abs(i/8 - j/8);    //棋盘上两格之间的相对位置

            y = abs(i%8 - j%8);

            if(x == 1 && y == 2 || x == 2 && y == 1)

                Knight[i][j] = Knight[j][i] = 1;    //只需跳一步

        }

        Knight[i][i] = 0;

    }

    for(k=0;k<64;k++)    //Floyd算法求任意两格之间的最短路径

    {

        for(i=0;i<64;i++)

        {

            for(j=0;j<64;j++)

            {

                if(Knight[i][k]+Knight[k][j]<Knight[i][j])

                    Knight[i][j]=Knight[i][k]+Knight[k][j];

            }

        }

    }

}



int main()

{

    work();    //保存任意两点之间的最短路径

    char str[6];

    int start,end;

    while(gets(str))

    {

        start = (str[0]-'a')*8 + str[1] - '1';

        end = (str[3]-'a')*8 + str[4] - '1';        

        printf("To get from %c%c to %c%c takes %d knight moves.\n",str[0],str[1],str[3],str[4],Knight[start][end]);

    }

    return 0;

}

 

你可能感兴趣的:(move)