求解一个问题:马的遍历

jiangqingtian 2010-12-14 12:50:15
具体题目:
在中国象棋棋盘上,对任一位置上放置的一个马,均能选择一个合适的路线,使得该棋子能按象棋的规则不重复地走过棋盘上的每一位置。
要求:
(1)依次输出所走过的各位置的坐标。
(2)最好能画出棋盘的图形形式,并在其上动态地标注行走过程。
(3)程序能方便地地移植到其它规格的棋盘上。

谢谢,麻烦用C++写,最好能告诉我一下原理!谢谢了哈
...全文
302 10 打赏 收藏 转发到动态 举报
写回复
用AI写文章
10 条回复
切换为时间正序
请发表友善的回复…
发表回复
fengnian2005 2011-02-23
  • 打赏
  • 举报
回复
其中一个解
4 1 3 0 2 5 7 6 -- 1 for trial order cpumax:1
5 3 4 6 7 2 1 0 -- 2 for trial order cpumax:1
=>0=>0=>1=>0=>7=>1=>3=>6=>1=>3=>4=>6=>1=>1=>6=>3=>1=>3=>0=>4=>6=>3=>6=>1=>1=>5=>4=>4=>4=>3=>2=>6=>3=>1=>7=>0=>1=>6=>3=>6=>1=>7=>5=>0=>4=>5=>1=>0=>5=>0=>5=>4=>3=>6=>4=>6=>2=>1=>3=>4=>6=>1=>2=>1 -- Solution 1

3 18 5 8 13 16 25 50

6 9 2 17 24 49 14 63

19 4 7 12 15 26 51 48

10 1 20 23 52 47 62 57

21 36 11 0 27 58 53 46

30 33 22 37 40 43 56 61

35 38 31 28 59 54 45 42

32 29 34 39 44 41 60 55

CPU: 2 sec === CPU for this order 0 sec -- Succeed after 2 attempts
fengnian2005 2011-02-23
  • 打赏
  • 举报
回复
前几天刚刚解决了这个问题,其实这个程序很好编,用递归法,我找到了1千万个解,至少还有1千万倍等着找出来呢。

但是成功非常需要运气,我运气背,走了弯路才发现的。

所谓的运气,就是马的八个可能走法,成功与否和走法的顺序有关,如果顺序随便顶好了,那么成功与否就决定于最开始的那个棋子放的位置,从每个位置开始,都可以有天文数字的解,但是一旦位置固定,某个顺序不到1秒就出来,有的24小时也出不来。同样的顺序,换个位置就出来,同样的位置,换个顺序就能出来。如果x,y坐标长度是1-8,起始坐标是4,4几十个小时也找不到,换到对称的位置5,5,或者4,5 或者5,4,可能不到1秒钟就能找到几千个解,10个小时可以找到1千万个不同的解。

如果要找到所有的解,按照我的CPU,需要至少2千年。

由于我一开始运气不好,顺序和位置刚好是短时间解出不来的,为了所短时间,我把程序复杂化了,找了很多重办提前看到死胡同,结果还是找不到,过了几天我偶然换了位置,立刻就找到了。由于不服气,我对不同顺序进行研究,发现有些1秒钟,有些两秒钟,有些30秒,有些几十个小时都没有等到。大约20%是可以在1分钟内找到的。

我发现我那些复杂化的制止思路步骤没有必要,但是也不是没有用,对于有解的那些顺序或者起始位置,时间缩短了好几倍,从1分钟能掉到20秒钟。

后来我干脆这有编写程序,让八个马步的搜索顺序是随机的,如果某个顺序1秒钟不出来,就换另外一个随机的,这有作就无论在哪个位置开始,都立刻或者不到几秒钟就找到解了。不过每次运行的解都是不一样的。

下面是我的程序,从注释的88行到149行那段是可以不要的,如果我运气好,就不需要编写的,甚至连整个防止死路的子程序available都可以不要。


#include <stdio.h>
#include <time.h>
#include <stdlib.h>

const int size=8,maxsol=1;
int map[size][size];
int dx[]={2,1,-1,-2,-2,-1,1,2};
int dy[]={1,2,2,1,-1,-2,-2,-1};
int step=0,stepmax=5,x00=4,y00=3,w[size*size+1];
int sol=0,fx[size*size+1],fy[size*size+1];
long int times=0, every=1000000;
int rstep[64][8],ro[8];
int repeat=0,cpumax=1;
/** good order
int ro0[]={ 2,3,4,5,6,7,0,1};
int ro0[]={ 6,7,0,1,2,3,4,5};
**/
int ro0[]={ 2,3,4,5,6,7,0,1};

time_t start,end; int dif=0, dif0=0;

int showresult() {
int x,y;
sol++;
for (int i=0; i<=step; i++) printf("=>%i",w[i]);
// printf ("\nStep %i (after %i solutions with stepmax=%i)\n",step,sol,stepmax);
printf (" -- Solution %i\n\n",sol);
for (x=0; x<size;x++) {
for (y=0; y<size;y++) { printf("%3i",map[x][y]);}
printf ("\n\n");
}
time(&end); dif = difftime (end,start); printf ("CPU: %4i sec === CPU for this order %3i sec",dif,dif-dif0);
if (sol>=maxsol) { printf(" -- Succeed after %i attempts\n",repeat); exit(0);}
printf ("\n\n");

}

int status() {
int x,y;
printf ("Step Print: ");
for (int i=0; i<=step; i++) printf("=>%i",w[i]);
printf ("\nStep %i (after %i solutions with stepmax=%i)\n",step,sol,stepmax);
for (x=0; x<size;x++) {
for (y=0; y<size;y++) { printf("%3i",map[x][y]);}
printf ("\n\n");
}
time(&end); dif = difftime (end,start); printf ("CPU: %i sec === CPU for this roder %i sec",dif,dif-dif0);
printf ("\n\n");
}

int available() {
int x,y, way, x1,y1,ava,extempid[size*size][8],pos[size*size],pext[size*size][8];
int exit[size*size],nept,minext;
int iept,ichk,ichk2,icon;
int *mpt; char i;
int none=0, ione,oneid[size*size];
int nsolo=0, soloid[size*size];
mpt = map[0];

// minext: This is the minimum number of expextid in which empty position is not going to accept an extra check

minext = 3; int npos=0;
if (step<15 || size*size-step-3 < minext || step>59) return 1;

ava=1; nept=0; int size0=0;
for (x=0; x<size; x++) {
for (y=0; y<size; y++) {
if (map[x][y]==-1) {
pos[nept]=x*size+y; exit[nept]=0;
//if (step>60||step>=stepmax-1) { printf("checking %i,%i for %i,%i in step %i .....\n",x,y,fx[step],fy[step],step);}
for (way=0; way<8; way++) {
x1 = x+dx[way]; y1 = y+dy[way];
if (x1<0 || x1>size-1) continue;
if (y1<0 || y1>size-1)continue;
if (map[x1][y1]==-1) {
pext[nept][exit[nept]]=y1+size*x1;
exit[nept]++;
}
}
nept++;
if (exit[nept-1]>0) continue; else return 0;
}
npos++;
}
}

// 整理鄰居關係 extempid 表示在空格iept的地ichkext個出口,是第ichkemp個空格
int uncheck[size*size], ichkemp,ichkext, theext;
for (iept=0; iept<nept; iept++) {
for ( ichkext=0; ichkext<exit[iept]; ichkext++) {
for (ichkemp=0; ichkemp<nept; ichkemp++) {
if (pos[ichkemp]==pext[iept][ichkext]) {
extempid[iept][ichkext]= ichkemp;
break;
}
}
}
//计数出口是1的空格
if (exit[iept] == 1) { oneid[none]=iept; none++; }
// 如果有超过2个空格只有1个出口,则跳过
if (none > 2) return 0;
}
// return 1;
// printf ("tracing %i---- ",times); status();
int extcom1;


for (iept=0; iept<nept; iept++) {
// if (uncheck[iept] > 0) continue;

/* 如果某個空格的出口比最小出口數多,那麼無需檢查*/
if (exit[iept]<=minext) {

/* 如果某個空格的出口的出口,比最小出口數多,那麼無需檢查,或者,出口的出口比较自己的出口多,要等到出口更多的空格检查*/
for (ichk=0; ichk<exit[iept]; ichk++) {
if (exit[extempid[iept][ichk]]>minext || exit[extempid[iept][ichk]] > exit[iept]) break;
}
/* 如果某個空格的出口的出口,比最小出口數少*/
if (ichk == exit[iept]) {
//尋找循環關係的死胡同
nsolo=0;
for (ichk2=0; ichk2<exit[iept]; ichk2++) {
// if (exit[extempid[iept][ichk2]]>extmax) extmax= exit[extempid[iept][ichk2]];
// 如果某空格有超过1个出口的出口为1,则跳过
if (exit[extempid[iept][ichk2]]==1) {soloid[nsolo]=ichk2,nsolo++;}
if (nsolo>1) return 0;

for (theext=0; theext<exit[extempid[iept][ichk2]]; ++theext) {
extcom1 = extempid[extempid[iept][ichk2]][theext];
if (extcom1==iept) {
continue;
}
for (icon=0; icon<exit[extempid[iept][icon]];icon++) {
if (extempid[iept][icon] != extempid[extempid[iept][ichk2]][theext]) break;
}
if (icon<exit[extempid[iept][icon]]) goto nocheck;
}
/*還有其他類型死胡同,暫不編程檢查*/
}

if (ichk2 < exit[iept]) continue;
return 0;

}
nocheck: continue;
}
}
return 1;
}

int skord() {

int i,j,k,rd,step;
int order0[size],order[size];
for (i=0; i<size; i++) {
order0[i]=i;
}

for (i=0;i<size-1;i++) {
rd = rand() % (size-i);
k=0;
for (j=0; j<size-i; j++) {
if (j==rd) {k++; ro[i]=order0[j];}
order[j]=order0[k++];
}
for (j=0; j<size-i-1; j++) {
order0[j]=order[j];
}
}
ro[i]=order0[0];
}

int move(int x0, int y0) {
int x,y,way;
time(&end); dif = difftime (end,start);
if (dif-dif0>cpumax) {
step--;
return step;
}
step++;
for (w[step]=0; w[step]<8; w[step]++) {
x=x0+dx[ro[w[step]]]; if (x>7 || x<0) continue;
y=y0+dy[ro[w[step]]]; if (y>7 || y<0) continue;
if (map[x][y] != -1) continue;
map[x][y]=step;
fx[step]=x;fy[step]=y;
int av=available();
if (av==1) {
if (step <size*size-1) {
move(x,y);
time(&end); dif = difftime (end,start);
if (dif-dif0>cpumax) {
step--;
return step;
}
} else showresult();
}
map[x][y]=-1;
}
if (step>stepmax) stepmax=step;
step--; times++; return step;

}

int main (int argc, char *argv[]) {
char *endptr;
if (argc > 1) cpumax = strtol(argv[1], &endptr, 10);


int x,y,x0,y0,i;
time(&start);
srand ( time(&start) );
//
while (sol<=maxsol+1) {
for (x=0; x<size; x++) for (y=0; y<size; y++) map[x][y]=-1;

skord();
for (i=0; i<size; i++) {
printf(" %i ",ro[i]);
}
printf (" -- %i for trial order cpumax:%i\n",++repeat,cpumax);


step = 0;
map[x00][y00]=0;
x0=x00; y0=y00;
dif0 =
move(x0,y0);
dif0 = dif;
}
printf ("Total %i solustion have been found",sol);
}
AAA20090987 2010-12-17
  • 打赏
  • 举报
回复
经典的NP问题,呵呵
iq02006 2010-12-17
  • 打赏
  • 举报
回复
没开发工具,随手写的,你自己实现几个函数就可以了

#define SIZEX 11
#define SIZEY 11
void Search(int bArrBeen[][] ,int x,int y,int step)
{
int cx,cy;
if(!PosAvailable(bArrBeen,x,y))//PosAvailable()判断位置是够合法
{
return ;
}

if(BeenEverywhere(bArrBeen))//BeenEverywhere() 判断是否走完,就是遍历值都大于0
{
//已经搜索到了,bArrBeen存的就是步骤,可以保存起来 或者直接输出,Search函数完全返回后这个数组的值会变成全0的

}

ShowStatus(bArrBeen);//你自己输出当前状态
ArrBeen[cx][cy]=++step;//标记步骤
search(bArrBeen,cx+1,cy+2,step);//总共八个方向 你自己补完下,我就写了2个
search(bArrBeen,cx+1,cy-2,step);
.......
ArrBeen[cx][cy]=0;//由于没有找到所以重置为0

}
}
jiangqingtian 2010-12-17
  • 打赏
  • 举报
回复
谢谢!
我大致明白了你的意思..
可是俺C++学的不好,数据库目前自己正在自学中...
没那个水平呀,但是这个题目我恰好又非常感兴趣..
我想的是
你能不能给我一个完成了80%的程序,剩下我自己边看书学习边完成,碰到不懂得地方我就问你..
这样可以么?谢谢了哈..非常感谢
iq02006 2010-12-17
  • 打赏
  • 举报
回复
我说个简单的方法 用回溯来实现 每次都搜索8个方向上可以走到位置,并标记为以走,到新的位置后继续搜索,直到搜索不到位置的时候判断是否全部标记为已走了,如果不是,就返回继续搜索, 楼主看一下回溯的概念,用递归很快就可以解决了
jiangqingtian 2010-12-17
  • 打赏
  • 举报
回复
话说我看不懂...
能帮写一个大致差不多的么?
然后我自己再看看,我不知道按什么思路来写..
谢谢了哈
Huntrees 2010-12-16
  • 打赏
  • 举报
回复
憋腿考虑么?
小楫轻舟 2010-12-14
  • 打赏
  • 举报
回复
http://blog.csdn.net/LightBoat09/archive/2010/07/25/5764713.aspx
这个类似,我以前写的,你看看能用到多少...
jiangqingtian 2010-12-14
  • 打赏
  • 举报
回复
[Quote=引用 1 楼 lightboat09 的回复:]
http://blog.csdn.net/LightBoat09/archive/2010/07/25/5764713.aspx
这个类似,我以前写的,你看看能用到多少...
[/Quote]
非常感谢!
我晚上慢慢看看哈!真的谢谢你了

33,311

社区成员

发帖
与我相关
我的任务
社区描述
C/C++ 新手乐园
社区管理员
  • 新手乐园社区
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

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