【算法】深度优先 马走日 Hamilton routes
在n*m的棋盘中,马只能走“日” 字。马从位置(x,y)处出发,把棋盘的每一格都走一次,且只走一次。找出所有路径。
×××××××××××××
类似问题:
在半个中国象棋棋盘上,马在左下角(1,1)处,马走日字…而且只能往右走…不能向左…可上可下…求从起点到(m, n)处有几种不同的走法。
八皇后。
×××××××××××××
预备知识---》图的深度优先:
https://www.cnblogs.com/OctoptusLian/p/8260173.html
参考代码1:
https://blog.csdn.net/skylv111/article/details/38930213
参考代码2(优先):
https://blog.csdn.net/qq_35654259/article/details/80644714
参考思考(图例):
https://blog.csdn.net/crayondeng/article/details/17174951
java版本:
https://maozj.iteye.com/blog/671911
***Hanmilton路径 主代码,不需要回到远点的请删除相关部分。8*8的国际象棋棋盘上的一只马,恰好走过除起点外的其他63个位置各一次,最后回到起点,这条路线称为马的一条Hamilton周游路线。对于给定的m*n的国际象棋棋盘,m和n均为大于5的偶数,且|m-n|≤2,试设计一个分治算法找出马的一条Hamilton周游路线。
#include<stdio.h> #include<stdlib.h> #define max 101 int m,n;//棋盘大小 int start_x,start_y;//起点位置 int dx[8]={-2,-1,1,2,-2,-1,2,1}; int dy[8]={-1,-2,-2,-1,1,2,1,2}; int board[max][max]={0}; int finish(int x,int y) {//判断是否是死路 if(x<1 || y<1 || x>m || y>n || board[x][y]!=0) return 0; else return 1; } int next_move(int x,int y) {//判断下一步能否回到起点 for(int i=0;i<8;i++) if(x+dx[i]==start_x && y+dy[i]==start_y) return 1; return 0; } void show(int n,int m) {//输出路线 for(int i=1;i<=m;i++) { for(int j=1;j<=n;j++) printf("%3d",board[i][j]); printf("\n"); } } void move(int x,int y,int num) { if(num==n*m && next_move(x,y)) { show(n,m); exit(1); } for(int i=0;i<8;i++) { int next_x=x+dx[i]; int next_y=y+dy[i]; if(finish(next_x,next_y)) { board[next_x][next_y]=num+1; move(next_x,next_y,num+1); board[next_x][next_y]=0; } } } int main() { printf("请输入棋盘的行数和列数:\n"); scanf("%d%d",&m,&n); printf("请输入起始坐标:\n"); scanf("%d%d",&start_x,&start_y); board[start_x][start_y]=1; int number=1; printf("马的周游路线为:\n"); move(start_x,start_y,number); return 0; }
//辅助理解
#include <stdio.h> #include <stdlib.h> #include <iostream> using namespace std; #define max 101 int count = 0; int m,n;//棋盘大小 int start_x,start_y;//起点位置 //考虑到马有8种走法 int dx[8]={-2,-1,1,2,-2,-1,2,1}; int dy[8]={-1,-2,-2,-1,1,2,1,2}; int board[max][max]={0}; //输出棋盘 void show(int m,int n){ for(int i = 0;i<m;i++){ for(int j = 0;j<n;j++){ cout<<board[i][j]<<" "; } cout<<endl; } } //判断下一步是否是起始的位置 int next_move(int x,int y){ for(int i = 0;i<8;i++){ if(board[x+dx[i]][y+dy[i]] == 1){//1表示马的起始位置 return 1; } } return 0; } //判断是否填了 int finish(int x,int y){ if(board[x][y] == 0){//0表示马没有走过 非0表示马已经走过 return 1; } return 0; } //马的下一步走法已经超出棋盘的范围了 int judge(int x,int y,int m,int n){ if(x>=0&&x<m&&y>=0&&y<n){ return 1; } return 0; } //马走的函数 void move(int x,int y,int num){ if(num == m*n+1&&next_move(x,y)){ cout<<++count<<endl; show(m,n);//输出棋盘 cout<<endl; return ; }else{ for(int i = 0;i<8;i++){ if(finish(x+dx[i],y+dy[i])&&judge(x+dx[i],y+dy[i],m,n)){//若不符合上述条件就表示马放弃之后会走这一步了 board[x+dx[i]][y+dy[i]] = num;//在棋盘上记录马的步数 move(x+dx[i],y+dy[i],num+1); board[x+dx[i]][y+dy[i]] = 0;//当遍历完棋盘后将棋盘重新置为0(表示马为走过) (不过开始位置还是1这里读者慢慢体会) } } } } int main(){ cout<<"请输入格子数"<<endl; cin>>m>>n; cout<<"请输入起始的位置"<<endl; cin>>start_x>>start_y; int number = 1; board[start_x][start_y] = number;//将起始位置为1 move(start_x,start_y,number+1); cout<<count; }
//上面的如果还理解不了,可以看这个。
#include <stdio.h>
#include <string.h> int matrix[10][9]; int journey = 1; int step_x[]={1,2,2,1,-1,-2,-2,-1},step_y[]={2,1,-1,-2,-2,-1,1,2}; void outMatrix(){ int i,j; for (i=0;i<10;i++) { for (j=0;j<9;j++) { printf("%-2d ",matrix[i][j]); } printf("\n"); } } bool outofbounds(int x,int y){ return x < 0 || y < 0 || x >= 10 || y >= 9; } bool isCome(int x,int y){ return matrix[x][y]; } void gotoend(int x, int y ){ if(journey>90) return; int i; matrix[x][y]=journey++; //当前是第几步 for (i = 0;i<8;i++) { int next_x = x+step_x[i]; int next_y = y+step_y[i]; if(!outofbounds(next_x,next_y) && !matrix[next_x][next_y]){ gotoend(next_x,next_y); } } } int main(){ int start_x,start_y; int i; scanf("%d%d",&start_x,&start_y); for (i = 0;i<10;i++) { memset(matrix[i],0,sizeof(matrix[0])); } gotoend(start_x,start_y); outMatrix(); return 0; }
//这个还有问题
package test; /** * create on 2010.05.21 TODO 回溯算法 * * @author 毛正吉 * @version v1.0 * */ public class RecollectionSearch { /** * @param args */ public static void main(String[] args) { // 注意(0<=x<n && 0<=y<m) int n = 5; int m = 4; int x = 0; int y = 0; RecollectionSearch rs = new RecollectionSearch(n, m, x, y); rs.find(x, y, 2); System.out.println("######################"); System.out.println("总解数count=" + rs.getCount()); System.out.println("######################"); } // 棋盘行数 private int n; // 棋盘列数 private int m; // 马的起始x坐标 private int x; // 马的起始y坐标 private int y; // 棋盘坐标 private int[][] a; // 求解总数 private int count; // "日"子x坐标 public int[] fx = { 1, 2, 2, 1, -1, -2, -2, -1 }; // "日"子y坐标 public int[] fy = { 2, 1, -1, -2, -2, -1, 1, 2 }; /** * 构造方法 * * @param _n * @param _m * @param _x * @param _y */ public RecollectionSearch(int _n, int _m, int _x, int _y) { n = _n; m = _m; x = _x; y = _y; a = new int[n][m]; for (int i = 0; i < n; i++) { for (int j = 0; j < m; j++) { a[i][j] = 0; } } // 马的起点 a[x][y] = 1; count = 0; } public void find(int x, int y, int dep) { int i, xx, yy; for (i = 0; i < 7; i++) { xx = x + fx[i]; yy = y + fy[i]; // 判断新坐标是否出界,是否已走过 if (check(xx, yy) == 1) { a[xx][yy] = dep; if (dep == n * m) { output(); } else { // 从新坐标出发,递归下一层 find(xx, yy, dep + 1); } // 回溯,恢复未走标志 a[xx][yy] = 0; } } } /** * 判断新坐标是否出界,是否已走过 * * @param xx * @param yy * @return */ public int check(int xx, int yy) { if (xx >= n || yy >= m || xx < 0 || yy < 0 || a[xx][yy] != 0) { return 0; } return 1; } /** * 输出结果 */ public void output() { count++; System.out.println("count=" + count); for (int y = 0; y < n; y++) { System.out.println(""); for (int x = 0; x < m; x++) { System.out.print(a[y][x] + " "); } } System.out.println(""); } public int getN() { return n; } public void setN(int n) { this.n = n; } public int getM() { return m; } public void setM(int m) { this.m = m; } public int getCount() { return count; } public void setCount(int count) { this.count = count; } public int getX() { return x; } public void setX(int x) { this.x = x; } public int getY() { return y; } public void setY(int y) { this.y = y; } }