马踏棋盘问题
2016-01-11 15:25
555 查看
马踏棋盘问题
问题描述
马踏棋盘主要的解决方案有两种:一种是基于深度优先搜索的方法,另一种是基于贪婪算法的方法。第一种基于深度优先搜索的方法是比较常用的算法,深度优先搜索算法也是数据结构中的经典算法之一,主要是回溯的算法思想,可采用递归实现。贪婪的算法则是一步一步依据当前最优的策略,依靠每一步的局部最优,达到最终目标。但是他不一定能够得到最优解。关于马踏棋盘的基本过程:国际象棋的棋盘为8*8的方格棋盘。现将”马”放在任意指定的方格中,按照”马”走棋的规则将”马”进行移动(如图所示,如果将空格标成点,就是象棋中的马走“日”字)。要求每个方格只能进入一次,最终使得”马”走遍棋盘的64个方格。如图所示,任意一个位置,“马”最多有8个方向可以跳动,所以每次都要依据这最多8个方向进行选择。
回溯法
深度优先搜索属于图算法的一种,英文缩写为DFS即Depth First Search.其过程简要来说是对每一个可能的分支路径深入到不能再深入为止,而且每个节点只能访问一次. (类似于树中的前序遍历,回溯法的思想)。
#include <stdio.h> #include <stdlib.h> #include <time.h> #define MATRIX 8 #define TRUE 1 #define ERROR 0 typedef int status ; int tag = 1; /*查找下一步方向*/ status nextStep(int (*chess)[MATRIX],int *x,int *y,int step){ switch(step) { case 0: if(*y+2<=MATRIX-1 && *x-1>=0 && chess[*x-1][*y+2]==0) { *y+=2; *x-=1; return TRUE; } break; case 1: if(*y+2<=MATRIX-1 && *x+1<=MATRIX-1 && chess[*x+1][*y+2]==0) { *y+=2; *x+=1; return TRUE; } break; case 2: if(*y+1<=MATRIX-1 && *x+2<=MATRIX-1 && chess[*x+2][*y+1]==0) { *y+=1; *x+=2; return TRUE; } break; case 3: if(*y-1>=0 && *x+2<=MATRIX-1 && chess[*x+2][*y-1]==0) { *y-=1; *x+=2; return TRUE; } break; case 4: if(*y-2>=0 && *x+1<=MATRIX-1 && chess[*x+1][*y-2]==0) { *y-=2; *x+=1; return TRUE; } break; case 5: if(*y-2>=0 && *x-1>=0 && chess[*x-1][*y-2]==0) { *y-=2; *x-=1; return TRUE; } break; case 6: if(*y-1>=0 && *x-2>=0 && chess[*x-2][*y-1]==0) { *y-=1; *x-=2; return TRUE; } break; case 7: if(*y+1<=MATRIX-1 && *x-2>=0 && chess[*x-2][*y+1]==0) { *y+=1; *x-=2; return TRUE; } break; default: break; } return ERROR; } /*DFS棋盘*/ void traverseChess(int (*chess)[MATRIX],int x,int y){ int count = 0; //最多8个方向计数 int x1 = x,y1 = y; if(x1<MATRIX && y1<MATRIX && chess[x1][y1] == 0){ chess[x1][y1] = tag; tag ++; } if(tag > MATRIX * MATRIX){ printf("oK!!task has accomplished...\n"); return; } for(count = 0;count<8;count++){ //总共有8个可能的方向 nextStep(chess,&x1,&y1,count); if(chess[x1][y1]==0)traverseChess(chess,x1,y1); //递归 } } /*打印棋盘*/ void print_chess(int (*chess)[MATRIX]){ int i = 0,j = 0; printf("\nThe MATRIX is as follow\n"); for(i = 0;i<MATRIX;i++){ for(j = 0;j < MATRIX;j++){ printf("%2d\t",chess[i][j]); } printf("\n"); } } void main(){ int i ,j; clock_t start ,end; int chess[MATRIX][MATRIX]; for(i = 0; i<MATRIX;i++){ for(j = 0;j < MATRIX;j++){ chess[i][j] = 0; //初始化 } } start = clock(); traverseChess(chess,4,0,1); //开始遍历 end = clock(); print_chess(chess); //打印棋盘 printf("共耗时:%f\n",(double)(end-start)/CLOCKS_PER_SEC); }
贪心算法
贪心算法(又称贪婪算法)是指,在对问题求解时,总是做出在当前看来是最好的选择。也就是说,不从整体最优上加以考虑,他所做出的是在某种意义上的局部最优解。贪心算法不是对所有问题都能得到整体最优解,关键是贪心策略的选择,选择的贪心策略必须具备无后效性,即某个状态以前的过程不会影响以后的状态,只与当前状态有关。
在处理马踏棋盘问题时,每次在至多8个可选的方向中,首先通过验证其中一个方向可以成功。贪心算法同样是试探至多的八个可能的出口,但是八个出口的排序并非按照前述的顺时针,而是按照每个可能出口的进一步出口数的递增排序,所以每次先试探的总是进一步出口较少的出口,能够给之后的出口更多的选择,因此贪心算法是比较高效的,而且它不回溯。
#include <stdio.h> #include <stdlib.h> #include <math.h> #include <time.h> #define ROWS 8 #define COLS 8 /*cursor i move chess*/ const int iktmove[8] = {-2,-1,1,2,2,1,-1,-2}; /*cursor j move chess*/ const int jktmove[8] = {1,2,2,1,-1,-2,-2,-1}; int chess[ROWS][COLS]; /*inital the chess*/ void chess_init(int chess[][COLS],int rows,int cols) { int i, j; for(i = 0; i < rows ; ++ i) for (j = 0; j < cols ; ++ j) { chess[i][j] = 0; } } /*print the chess*/ void print_chess(int chess[][COLS]) { int i,j; for(i = 0; i < ROWS; ++ i) { for(j = 0; j < COLS; ++ j) printf("%d\t",chess[i][j]); printf("\n\n"); } } /*find the index of min non-zeros*/ int minindex(int a[],int cols) { int i = 0,index = 0; int min = a[0]; for(i = 0 ; i< cols; ++ i) { if(a[i] >0) { min = a[i]; index = i; break; } } for(i = index + 1; i < cols ; ++ i) { if(a[i] > 0 && min > a[i]) { min = a[i]; index = i; } } if(a[index] > 0) return index; return -1; } /**/ void greedy_chess(int chess[][COLS],int rows,int cols,int start_i,int start_j) { int i,npos,m,min,j,nnpos; int nexti[8] = {0,0,0,0,0,0,0,0}; int nextj[8] = {0,0,0,0,0,0,0,0}; int exit[8] = {0,0,0,0,0,0,0,0}; /*init*/ chess_init(chess,rows,cols); /*first step*/ chess[start_i][start_j] = 1; /*n>1 step*/ for(m = 1; m < 64; ++ m) { /*steps d*/ npos = 0; for(i = 0; i < 8; ++ i) { /*ignore the point which doesn't satisfy the conditions*/ if( start_i + iktmove[i] < 0 || start_i + iktmove[i] >= rows || start_j + jktmove[i] < 0 || start_j + jktmove[i] >= cols || chess[start_i + iktmove[i]][start_j+jktmove[i]] > 0) { continue; } /*save the point which satisfy the conditions*/ nexti[npos] = start_i + iktmove[i]; nextj[npos] = start_j + jktmove[i]; /*statistics how many point satisfy conditions*/ npos ++; } /*steps e*/ if(npos == 0) { printf("Can not finish the game!!\n,The steps of game can be %d\n",m); return; } /*steps e*/ if(npos == 1) { min = 0; start_i = nexti[min]; start_j = nextj[min]; chess[start_i][start_j] = m+1; } /*steps f*/ if(npos > 1) { for(i = 0; i < npos ; ++ i) { nnpos = 0; for(j = 0; j < 8; ++ j) { if( nexti[i] + iktmove[j] >= 0 && nexti[i] + iktmove[j] < rows && nextj[i] + jktmove[j] >= 0 && nextj[i] + jktmove[j] < cols && chess[nexti[i] + iktmove[j]][nextj[i] + jktmove[j]] == 0) { nnpos ++; } } exit[i] = nnpos; } if((min = minindex(exit,npos)) >= 0) { start_i = nexti[min]; start_j = nextj[min]; chess[start_i][start_j] = m+1; } else { printf("Can not finish the game!!\n,The steps of game can be %d\n",m); return; } } } } void main() { clock_t start,end; start = clock(); greedy_chess(chess,ROWS,COLS,5,1); end = clock(); print_chess(chess); printf("time consumed:%f\n",(double)(end-start)/CLOCKS_PER_SEC); }
相关文章推荐
- 有关数据库SQL递归查询在不同数据库中的实现方法
- C#中的递归APS和CPS模式详解
- C#使用回溯法解决背包问题实例分析
- WinForm实现按名称递归查找控件的方法
- 使用SqlServer CTE递归查询处理树、图和层次结构
- C#中的尾递归与Continuation详解
- C#递归实现显示文件夹及所有文件并计算其大小的方法
- php递归创建目录的方法
- PHP递归创建多级目录
- 贪心算法的C语言实现与运用详解
- Javascript递归打印Document层次关系实例分析
- javascript递归回溯法解八皇后问题
- oracle 使用递归的性能提示测试对比
- 使用curl递归下载软件脚本分享
- Perl脚本实现递归遍历目录下的文件
- JavaScript的递归之递归与循环示例介绍
- C# 递归查找树状目录实现方法
- 全排列算法的非递归实现与递归实现的方法(C++)
- php递归列出所有文件和目录的代码
- java递归菜单树转换成pojo对象