国际象棋中马的周游路线问题的递归算法 程序问题

heymanandgirl 2012-08-11 05:16:20
#include<stdio.h>
int H[8][8] = {0}; //全局变量的二维数组,用以保存马走步的放置情况,其中每个元素存放走步的序号,
//初值为0
int P[8][8] = {0}; //全局变量的二维数组,对应于棋盘上各个位置可以选择的位置数,即邻接点的度


struct D {
int x;
int y;
}DIR[8];

void go(int x,int y,int i) {
int min,j,u,v,u1,v1,m,n;
if(i == 65) { //递归出口,若棋盘上的每个位置都走过一遍,则算法结束
for(m = 0;m < 8;m++)
{
printf("\n");
for(n = 0;n < 8;n++)
printf("%5d",H[m][n]);
}

}
else {
min = 8;
for(j = 0;j < 8;j++) {
u = x + DIR[j].x;
v = y + DIR[j].y;
if((u >= 0) && (u < 8) && (v >= 0) && (v < 8) && (H[u][v] == 0)) {
//判断所走的位置是否超出棋盘应有位置
P[u][v] = P[u][v] - 1; //其邻接点的度减一。
if(P[u][v] < min) {
min = P[u][v];
u1 = u;
v1 = v;
}
}
}
H[u1][v1] = i;
go(u1,v1,i+1);//用满足条件的新顶点作为起点,继续递归调用
}
}

int main(void) {
int m,n;
printf("**********马得周游路线*********");
printf("说明:以下面的模拟棋盘为例:\n");
for(m = 0;m < 8;m++) {
printf("\n");
for(n = 0;n < 8;n++)
printf("%d%d ",m,n);
}
printf("\n\n请输入马得当前位置:");
scanf("%d,%d",&m,&n);
printf("\n");
H[m][n] = 1;
go(m,n,2);
printf("\n");

return 0;
}


大虾们请帮我看看,程序哪里出了点问题啊?
...全文
780 10 打赏 收藏 转发到动态 举报
写回复
用AI写文章
10 条回复
切换为时间正序
请发表友善的回复…
发表回复
heymanandgirl 2012-08-14
  • 打赏
  • 举报
回复
8楼的兄弟,谢谢你啦,你真给力...
j8daxue 2012-08-13
  • 打赏
  • 举报
回复
[Quote=引用 8 楼 的回复:]
引用 5 楼 的回复:
刚开始的时候,我也考虑过用dfs方法,解这个题,但是,各位楼主也知道,一旦,顶点的个数较多时,这样的效率就不是很高啦。我在想能不能算计到另外一种比较好的方法,让大家见笑了,自己没有想到,但是,在网上看到一篇关于这样的的算法比较好,是基于度的递归算法。楼主不访看一下:http://www.docin.com/p-167901698.html 看能不能利用这个方法解这个题目。……
[/Quote]
真有耐心,支持下。
kingxuke 2012-08-12
  • 打赏
  • 举报
回复
[Quote=引用 5 楼 的回复:]
刚开始的时候,我也考虑过用dfs方法,解这个题,但是,各位楼主也知道,一旦,顶点的个数较多时,这样的效率就不是很高啦。我在想能不能算计到另外一种比较好的方法,让大家见笑了,自己没有想到,但是,在网上看到一篇关于这样的的算法比较好,是基于度的递归算法。楼主不访看一下:http://www.docin.com/p-167901698.html 看能不能利用这个方法解这个题目。呵呵...楼主们,谢啦!
[/Quote]
**********************************先给说明,再贴代码**********************************

看了楼主提供的链接的论文,基本思想就是使用 Warnsdorff 原则来找下一个位置。简单来说,就是先将最难的位置走完,接下来的路相对就容易走了,因此也更可能找到周游路径。这里所谓的最难走的位置,实际上就是具有最小临接度(可走位置最小)的位置。
Warnsdorff 原则的缺陷,Warnsdorff 方法是一种启发式的算法,因此尽管它可以以较高的概率找出一种可行的走法,但有可能找不到解决方法(但在8*8的棋盘上证明是有效的);此外,它不能用于找到所有可行的方法(找所有的路径,还是DFS吧),它通常用于找出一条可行路径。

下面是楼主提供的链接中的论文的实现代码(只能找出一条路径,但效率非常高):

#include<stdio.h>

int H[8][8] = {0}; // 全局变量的二维数组,用以保存马走步的放置情况
// 其中每个元素存放走步的序号,初值为 0
int P[8][8] = {0}; // 全局变量的二维数组,对应于棋盘上各个位置可以
// 选择的位置数,即邻接点的度
bool g_find_solution = false; // 标示是否找到了走法


// 位于[x,y]位置的马,需要检测 8 个方向来确定下一个位置, 如下图:
//
// 2 3 方向1: [x-1,y-2]; 方向2: [x-2,y-1];
// 1 4 方向3: [x-2,y+1]; 方向4: [x-1,y+2];
// H<---------[x,y] 方向5: [x+1,y+2]; 方向6: [x+2,y+1];
// 8 5 方向7: [x+2,y-1]; 方向8: [x+1,y-2];
// 7 6 注:x 为行,y 为列,探测方向:1-8

struct D { // 通过结构体来定义 8 个方向
int x;
int y;
}DIR[8] = {
{-1,-2}, {-2,-1}, {-2,1}, {-1,2}, // 方向:1,2,3,4
{1,2}, {2,1}, {2,-1}, {1,-2} // 方向:5,6,7,8
};

void InitP(int initX, int initY) // 填充棋盘上每个位置的选择度数
{
int i,j,k; // 临时变量
int u,v; // 保存探测位置的坐标

for (i=0; i<8; i++) { // 行递增
for(j=0; j<8; j++) { // 列递增
for(k=0; k<8; k++) { // 探测 8 个方向
u = i + DIR[k].x;
v = j + DIR[k].y;
if( (u >= 0) && (u < 8) && // 行方向合法性判断
(v >= 0) && (v < 8) ) { // 列方向合法性判断
P[i][j] ++; // 位置的度数加 1
}
}
}
}

P[initX][initY] = 0; // 将初始位置的选择度数置为 0
}

void go(int x,int y,int i) // 搜索第 i 步的位置
{
int j,m,n; // 临时变量
int min; // 保存最小临接度数
int u,v; // 保存探测位置坐标
int u1 = -1,v1 = -1; // 保存有最小临接度数的位置坐标

if(i == 65) { //递归出口,若棋盘上的每个位置都走过一遍,则算法结束
g_find_solution = true;

for(m = 0;m < 8;m++) {
printf("\n");
for(n = 0;n < 8;n++)
printf("%5d",H[m][n]);
}
printf("\n\n");
}
else { // 每一步都探测 8 个方向来确定下一个位置
min = 8; // 初始最小临接度数为最大值 8

for(j = 0;j < 8;j++) { // 遍历 8 个方向寻找有最小临接度数位置
u = x + DIR[j].x; // 第 j 个方向的行坐标
v = y + DIR[j].y; // 第 j 个方向的列坐标
if( (u >= 0) && (u < 8) && // 行方向合法性判断
(v >= 0) && (v < 8) && // 列方向合法性判断
(H[u][v] == 0) ) { // 是否可走判断
P[u][v] --; // 其邻接点的度减 1
if(P[u][v] < min) {
min = P[u][v];
u1 = u; // 保存有最小临接度数位置的行坐标
v1 = v; // 保存有最小临接度数位置的列坐标
}
}
}

if(u1 == -1 || v1 == -1) { // 8 个方向都不满足,周游搜索失败
return;
}

H[u1][v1] = i; // 用有最小临接度数位置坐标设置当前的步序号
go(u1,v1,i+1); // 并将其作为下一步的起点,继续递归调用
}
}

int main(void)
{
int m,n;

printf("**********马的周游路线*********");
printf("说明:以下面的模拟棋盘为例:\n");
for(m = 0;m < 8;m++) {
printf("\n");
for(n = 0;n < 8;n++)
printf("%d%d ",m,n);
}

printf("\n\n请输入马的当前位置,格式:x,y (其中 0=<x<8, 0=<y<8):");
scanf("%d,%d",&m,&n);
while (m<0 || m>8 || n<0 || n>8)
{
printf("\n\n你的输入有误!");
printf("\n\n请输入马的当前位置,格式:x,y (其中 0=<x<8, 0=<y<8):");
scanf("%d,%d",&m,&n);
}

printf("\n");
H[m][n] = 1;
InitP(m,n);
go(m,n,2);
printf("\n");

if (!g_find_solution) {
printf("\n\n从输入点 (%d, %d) 开始,没有找到可行的路径!", m, n);
}

return 0;
}
// 输出结果:
/*
**********马的周游路线*********说明:以下面的模拟棋盘为例:

00 01 02 03 04 05 06 07
10 11 12 13 14 15 16 17
20 21 22 23 24 25 26 27
30 31 32 33 34 35 36 37
40 41 42 43 44 45 46 47
50 51 52 53 54 55 56 57
60 61 62 63 64 65 66 67
70 71 72 73 74 75 76 77

请输入马的当前位置,格式:x,y (其中 0=<x<8, 0=<y<8):1,2


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


请按任意键继续. . .
*/

代码中有非常详细的注释,希望楼主能够理解。Good Luck!
heymanandgirl 2012-08-12
  • 打赏
  • 举报
回复
怎么这么说呢!哪有无视啊!很感谢4F兄弟.
j8daxue 2012-08-12
  • 打赏
  • 举报
回复
[Quote=引用 5 楼 的回复:]
刚开始的时候,我也考虑过用dfs方法,解这个题,但是,各位楼主也知道,一旦,顶点的个数较多时,这样的效率就不是很高啦。我在想能不能算计到另外一种比较好的方法,让大家见笑了,自己没有想到,但是,在网上看到一篇关于这样的的算法比较好,是基于度的递归算法。楼主不访看一下:http://www.docin.com/p-167901698.html 看能不能利用这个方法解这个题目。呵呵...楼主们,谢啦!
[/Quote]
4F已经说了类似的做法,LZ看来是无视了。原题为http://soj.me/1152。自己搜题解
heymanandgirl 2012-08-12
  • 打赏
  • 举报
回复
刚开始的时候,我也考虑过用dfs方法,解这个题,但是,各位楼主也知道,一旦,顶点的个数较多时,这样的效率就不是很高啦。我在想能不能算计到另外一种比较好的方法,让大家见笑了,自己没有想到,但是,在网上看到一篇关于这样的的算法比较好,是基于度的递归算法。楼主不访看一下:http://www.docin.com/p-167901698.html 看能不能利用这个方法解这个题目。呵呵...楼主们,谢啦!
j8daxue 2012-08-12
  • 打赏
  • 举报
回复
[Quote=引用 2 楼 的回复:]
楼主,很认真的看了你的代码,大概理解了你的意思,但是你的实现却令人难以理解,尤其这里定义的全局变量 P 和 局部变量 min,实在不明白它们的意义。 下面给出一个和楼主类似的实现,里面有详细的注释:

C/C++ code

#include<stdio.h>

// 注: 当为 8*8 的棋盘时,运行非常慢,在我电脑上要将近10分钟
// 可以用较小的数据测试,……
[/Quote]

soj上的一题,盲目的dfs必然超时,因为太深了,一旦选错方向出来就困难了。
搜索应该优先搜索下层方向中,可扩展节点数量最小的。
即按这个值排序后进入下层。
kingxuke 2012-08-11
  • 打赏
  • 举报
回复
上面代码中有两处笔误,请楼主改正:

printf("**********马得周游路线*********"); // 应该改为:
printf("**********马的周游路线*********");

printf("\n\n请输入马得当前位置,格式:x,y (其中 0=<x<%d, 0=<y<%d):",R,C); // 改为:
printf("\n\n请输入马的当前位置,格式:x,y (其中 0=<x<%d, 0=<y<%d):",R,C);

楼主,Good Luck!
kingxuke 2012-08-11
  • 打赏
  • 举报
回复
楼主,很认真的看了你的代码,大概理解了你的意思,但是你的实现却令人难以理解,尤其这里定义的全局变量 P 和 局部变量 min,实在不明白它们的意义。 下面给出一个和楼主类似的实现,里面有详细的注释:

#include<stdio.h>

// 注: 当为 8*8 的棋盘时,运行非常慢,在我电脑上要将近10分钟
// 可以用较小的数据测试,如 7*7 ,6*6 或 6*7等

#define R 8 // 棋盘的行数
#define C 8 // 棋盘的列数
#define N (R*C+1) // 最大序号+1

int H[R][C] = {0}; // 全局变量的二维数组,用以保存马走步的放置情况
// 其中每个元素存放走步的序号,初值为0

bool g_just_find_one = false; // 标示是否只找一个走法,还是所有的
bool g_find_solution = false; // 标示是否找到了走法

// 位于[x,y]位置的马,需要检测 8 个方向来确定下一个位置, 如下图:
//
// 2 3 方向1: [x-1,y-2]; 方向2: [x-2,y-1];
// 1 4 方向3: [x-2,y+1]; 方向4: [x-1,y+2];
// H<---------[x,y] 方向5: [x+1,y+2]; 方向6: [x+2,y+1];
// 8 5 方向7: [x+2,y-1]; 方向8: [x+1,y-2];
// 7 6 注:x 为行,y 为列,探测方向:1-8


struct D { // 通过结构体来定义 8 个方向
int x;
int y;
}DIR[8] = {
{-1,-2}, {-2,-1}, {-2,1}, {-1,2}, // 方向:1,2,3,4
{1,2}, {2,1}, {2,-1}, {1,-2} // 方向:5,6,7,8
};


void go(int x,int y,int i) { // 搜索第 i 步的位置

int j,u,v,m,n;

if (g_just_find_one) { // 如果只找一个走法

if(g_find_solution) {
return;
}
}

if(i == N) { // 递归出口,若棋盘上的每个位置都走过一遍,则算法结束
g_find_solution = true;

for(m = 0;m < R;m++) {
printf("\n");
for(n = 0;n < C;n++)
printf("%5d",H[m][n]);
}
printf("\n\n\n");
}
else { // 每一步都探测8个方向来确定下一个位置
for(j = 0;j < 8;j++) {

u = x + DIR[j].x;
v = y + DIR[j].y;

if( (u >= 0) && (u < R) && // 行方向合法性判断
(v >= 0) && (v < C) && // 列方向合法性判断
(H[u][v] == 0) ) { // 是否可走判断

H[u][v] = i; // 位置合法,将该位置设置为当前步的序号
go(u,v,i+1); // 从当前位置走下一步

H[u][v] = 0; // 上一个方向探测结束后,将探测过的位置复位
// 以进行下一个方向的探测
}
}
}
}

int main(void) {
int m,n;
char ch;
printf("**********马得周游路线*********");
printf("说明:以下面的模拟棋盘为例:\n");
for(m = 0;m < R;m++) {
printf("\n");
for(n = 0;n < C;n++)
printf("%d%d ",m,n);
}

printf("\n\n请输入马的当前位置,格式:x,y (其中 0=<x<%d, 0=<y<%d):",R,C);
scanf("%d,%d",&m,&n);
while (m<0 || m>R || n<0 || n>C)
{
printf("\n\n你的输入有误!");
printf("\n\n请输入马得当前位置,格式:x,y (其中 0=<x<%d, 0=<y<%d):",R,C);
scanf("%d,%d",&m,&n);
}

getchar();

printf("\n\n是否只要求一个解法(Y 或 y 表示是,其它键表示否):");
scanf("%c",&ch);
if (ch == 'Y' || ch == 'y') {
g_just_find_one = true;
}

printf("\n");
H[m][n] = 1;
go(m,n,2);
printf("\n");
if (!g_find_solution) {
printf("\n\n从输入点 (%d, %d) 开始,没有可行的路径!", m, n);
}

return 0;
}
// 输出示例:
/*
**********马得周游路线*********说明:以下面的模拟棋盘为例:

00 01 02 03 04 05 06 07
10 11 12 13 14 15 16 17
20 21 22 23 24 25 26 27
30 31 32 33 34 35 36 37
40 41 42 43 44 45 46 47
50 51 52 53 54 55 56 57
60 61 62 63 64 65 66 67
70 71 72 73 74 75 76 77

请输入马的当前位置,格式:x,y (其中 0=<x<8, 0=<y<8):2,0


是否只要求一个解法(Y 或 y 表示是,其它键表示否):y


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


请按任意键继续. . .
*/

代码里面有详细的注释和一些运行时间的说明,希望楼主可以理解。Good Luck!
MC_LoveX 2012-08-11
  • 打赏
  • 举报
回复
先不谈算法就go函数里面u1,v1没有初始化就使用 程序必死

64,642

社区成员

发帖
与我相关
我的任务
社区描述
C++ 语言相关问题讨论,技术干货分享,前沿动态等
c++ 技术论坛(原bbs)
社区管理员
  • C++ 语言社区
  • encoderlee
  • paschen
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
  1. 请不要发布与C++技术无关的贴子
  2. 请不要发布与技术无关的招聘、广告的帖子
  3. 请尽可能的描述清楚你的问题,如果涉及到代码请尽可能的格式化一下

试试用AI创作助手写篇文章吧