[C语言][数据结构]马踏棋盘问题

10 篇文章 11 订阅
9 篇文章 0 订阅

将马放入国际象棋8×8棋盘的制定的某个方格中,马按走棋规则进行移动,要求每个方格只进入一次,走遍棋盘上全部64个方格。求出马的行走路线,并按求出的行走路线将数字1,2,…,64依次填入一个8×8的方阵并输出。 特别规定,马的8个探索方向如下图所示的1至8的顺序探索。请添加图片描述

输入格式:

输入两个整数 m 和n(空格间隔),表示起始行列号。

输出格式:

以矩阵形式将8×8棋盘中的路线输出。 每个数自占两个字符的宽度(不足两位时,右对齐),每个数后面空一格。

输入样例1:

1 1
//结尾无空行

输出样例1:

 1 38 55 34  3 36 19 22 
54 47  2 37 20 23  4 17 
39 56 33 46 35 18 21 10 
48 53 40 57 24 11 16  5 
59 32 45 52 41 26  9 12 
44 49 58 25 62 15  6 27 
31 60 51 42 29  8 13 64 
50 43 30 61 14 63 28  7 
//结尾无空行

输入样例2:

4 1
//结尾无空行

输出样例2:

38 49 56  3 30 41 14  5 
57  2 39 42 55  4 31 12 
50 37 48 29 40 13  6 15 
 1 58 43 54 27 32 11 20 
36 51 28 47 62 21 16  7 
59 44 53 26 33 10 19 22 
52 35 46 61 24 63  8 17 
45 60 25 34  9 18 23 64 
//结尾无空行

思路与代码:

定义马位置(x,y)坐标的结构体:

typedef struct {
    int x;
    int y;
} Coordinate;

定义棋盘:

int chessboard[8][8]={0};

定义查找方向结构体:
因为规定了查找方向的顺序,所以查找方向也要按照规定顺序:

Coordinate move[8] = {{-2,1},{-1,2},{1,2},{2,1},{2,-1},{1,-2},{-1,-2},{-2,-1}};

定义栈内点信息的结构体:

typedef struct {
    int ord;
    //顺序(第几部)
    Coordinate seat;
    //位置
    int di;
    //查找方向
} SElemType;

定义栈:

typedef struct {
    SElemType *base;
    SElemType *top;
    int stacksize;
} SqStack;

初始化栈:

int  InitStack(SqStack *s1) {
    s1->base=(SElemType *)malloc(STACK_INIT_SIZE*sizeof(SElemType));
    if(!s1->base) {
        exit(1);
    }
    s1->top=s1->base;
    s1->stacksize = STACK_INIT_SIZE;
    return 1;
}

可以看到:base保存了栈数组的首地址,top保存的是栈顶元素的下一位的地址,stacksize保存的是栈的长度。
出栈pop逻辑较为简单:
只需要

SElemType Pop(SqStack*s,SElemType m) {
    m = *(--s->top);
    //top位置移动,移到被弹出的栈顶元素位置
    //顺势输出
    return m;
}

入栈逻辑:

int Push(SqStack *s1,SElemType m) {
    if(s1->top-s1->base>=s1->stacksize) {
    //如果栈满,则增加栈长度
        s1->base=(SElemType *)realloc(s1->base,(s1->stacksize+n)*sizeof(SElemType));
        if(!s1->base)
            exit (1);
        s1->top=s1->base+s1->stacksize;
        s1->stacksize+=n;
    }
    //直接入栈
    *(s1->top++)=m;
    return 1;
}

计算下一个位置:
传入当前点与搜索方向

Coordinate NextPos(Coordinate s,int i) {
    s.x = s.x+move[i].x;
    s.y = s.y+move[i].y;
    return(s);
}

判断点是否有效(在棋盘内),是则返回1:

int Pass(Coordinate s) {
    if(chessboard[s.x][s.y]==0&&(s.x<=7)&&(s.x>=0)&&(s.y<=7)&&(s.y>=0)) {
        return 1;
    } else {
        return 0;
    }
}

主函数:

int main() {
    int i,j;
    Coordinate start;
    scanf("%d %d", &start.x, &start.y);
    start.x -= 1;
    start.y -= 1;
    //核心函数
    knight(start);
    for (i = 0; i < 8; i++) {
        for(j = 0; j < 8; j++) {
            printf("%2d ", chessboard[i][j]);
        }
        printf("\n");
    }
    return 0;
}

核心函数:

void knight(Coordinate start) {
    int curstep = 0;
    SqStack S;
    SElemType e;
    Coordinate curpos = start;
    InitStack(&S);
    do {
        if(Pass(curpos)) {
        //curpos在棋盘内
            curstep++;
            chessboard[curpos.x][curpos.y] = curstep;
            e.seat = curpos;
            e.ord = curstep;
            e.di = 0;
            //初始化点信息
            Push(&S, e);
            //入栈
            if(curstep == 64) {
            //找到64个即结束
                break;
            } else {
            //否则找下一个点
                curpos=NextPos(curpos, e.di);}
        } else {
        //不在棋盘内
            if(!StackEmpty(&S)) {
                Pop(&S, e);
                if(e.di == 7) {
                    chessboard[e.seat.x][e.seat.y]=0;
                }
                while(e.di == 7 && !StackEmpty(&S)) {
                    e = Pop(&S, e);
                    //八个方向都找过,则说明这步走不到结束,那么出栈,将棋盘重置,需要更换上一步的走法
                    if(e.di == 7) {
                        chessboard[e.seat.x][e.seat.y] = 0;
                    }
                    curstep = e.ord;
                }
                if(e.di < 7) {
                //八个方向没找满,继续找下一个方向
                    e.di++;
                    Push(&S, e);
                    curpos = NextPos(e.seat, e.di);
                }
            }
        }
    } while(!StackEmpty(&S));
    //非空一直走
}
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值