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;
}

 

posted on 2012-09-02 22:07  pcoda  阅读(1129)  评论(0编辑  收藏  举报