马周游问题(Java实现)

马周游问题

1. 问题描述

在一个n*n的棋盘中的某个位置有一只马,如果它走n*n步正好经过除起点外的其他位置各一次,这样一种走法则称马的周游路线,试设计一个算法,从给定的起点出发,找出它的一条周游路线。

2. 回溯法的一般思路

对于用回溯法求解的问题,首先要将问题进行适当的转化,得出状态空间树。 这棵树的每条完整路径都代表了一种解的可能。通过深度优先搜索这棵树,枚举每种可能的解的情况;从而得出结果。但是,回溯法中通过构造约束函数,可以大大 提升程序效率,因为在深度优先搜索的过程中,不断的将每个解(并不一定是完整的,事实上这也就是构造约束函数的意义所在)与约束函数进行对照从而删除一些 不可能的解,这样就不必继续把解的剩余部分列出从而节省部分时间。

3. 求解问题的回溯算法描述

1.按顺时针方向搜索当前点的8个方向,如果没有走过的就摘取下来。

2.判断点的个数是否为0,否的继续执行3,为0转5

3.对摘下来的点进行打分,规制:入度少的为第一优先级,靠边缘的为第二优先级。

4.根据分数获取分数最少的点(分数少的优先)。转到1

5.判断栈中的保存的点是否为空,如果为空,退出程序。如果不为空,就退一步,转到1.

4. 算法实现的关键技巧

1.判断要走的下一个点还有多少个点可以到达这个点,少的优先。

2.条件1相同的话,靠边的先走。

3.不管输入的点是哪一个,都是从中间位置开始,最后通过位移算出从输入那个点的路径。(对于这点,很多人都是不明白,我也不明白那些人为什么不明白,其实是一个很简单的道理。因为你最后走出来的是一个回路,不管那个棋盘是怎样的,最后你肯定可以把它变成一个环,环的每一个结点就是由棋盘中的格子和步数组成,在不移动格子的情况下,对数字进行转圈,然后再按拆开的办法放回去就会发现数字已经移动了)废话多了

下面贴代码,代码有三部分组成,用的是面向对象的方法

KnightTourPro类 负责运行程序

MPoint类 棋盘格子

ShowInfoWindow类 负责显示运行后的效果

KinghtTourPro

   1:  package nom.auggie.Knight;
   2:   
   3:  import java.awt.Point;
   4:  import java.util.Scanner;
   5:   
   6:  import nom.auggie.Knight.gui.ShowInfoWindow;
   7:   
   8:  public class KnightTourPro {
   9:   
  10:      static final int NO_PASS = 0;
  11:      /**
  12:       * 代表8个方向,顺时针方向
  13:       */
  14:      private static final Point[] dirs = { new Point(-2, 1), new Point(-1, 2), new Point(1, 2),
  15:              new Point(2, 1), new Point(2, -1), new Point(1, -2), new Point(-1, -2),
  16:              new Point(-2, -1) };
  17:   
  18:      /**
  19:       * 矩阵大小
  20:       */
  21:      private int size;
  22:   
  23:      /**
  24:       * 栈指针
  25:       */
  26:      private int stackL = -1;
  27:   
  28:      /**
  29:       * 记录开始点
  30:       */
  31:      private MPoint oPt = new MPoint();
  32:      /**
  33:       * 当前的步数
  34:       */
  35:      private int step = 1;
  36:   
  37:      /**
  38:       * 点矩阵矩阵
  39:       */
  40:      private MPoint[][] mPath;
  41:   
  42:      /**
  43:       * 当前点的位置
  44:       */
  45:      private MPoint lPoint;
  46:   
  47:      /**
  48:       * 保存走过的点
  49:       */
  50:      private MPoint[] stackPoint;
  51:   
  52:      public KnightTourPro(int size) {
  53:          this.size = size;
  54:          this.mPath = new MPoint[size][size];
  55:          for (int i = 0; i < mPath.length; i++) {
  56:              for (int j = 0; j < mPath.length; j++) {
  57:                  mPath[i][j] = new MPoint(i, j);
  58:                  this.getPSScore(mPath[i][j]);
  59:              }
  60:          }
  61:          this.stackPoint = new MPoint[size * size];
  62:      }
  63:   
  64:      /**
  65:       * 开始执行
  66:       * 
  67:       * @return
  68:       */
  69:      public int[][] doWalk(int x, int y) {
  70:          oPt.x = x;
  71:          oPt.y = y;
  72:          lPoint = new MPoint(x, y);
  73:          stackPoint[++stackL] = this.lPoint;
  74:          mPath[this.lPoint.x][this.lPoint.y].stepNum = step++;
  75:          int i=0;
  76:          do {
  77:              if (!this.goNext()) {
  78:                  if (!this.goBack()) {
  79:                      if(i==0){ this.debugPrint();i++;}
  80:                      break;
  81:                  }
  82:              }
  83:              // debugPrint();
  84:              if (step - 1 == size * size) {
  85:                  if (isComeBack()) break;
  86:              }
  87:          } while (true);
  88:          if (step < 1) return null;
  89:          return this.toIntMatrix();
  90:      }
  91:   
  92:      private boolean isComeBack() {
  93:          // System.out.println("isComeBack");
  94:          int x = stackPoint[stackL].x;
  95:          int y = stackPoint[stackL].y;
  96:          for (int i = 0; i < 8; i++) {
  97:              if ((x + dirs[i].x) == oPt.x && (y + dirs[i].y) == oPt.y) { return true; }
  98:          }
  99:          return false;
 100:      }
 101:   
 102:      /**
 103:       * 调试用的
 104:       */
 105:      private void debugPrint() {
 106:          int[][] mPath = this.toIntMatrix();
 107:          for (int[] i : mPath) {
 108:              for (int j : i) {
 109:                  System.out.print(j + " ");
 110:              }
 111:              System.out.println();
 112:          }
 113:          System.out.println();
 114:          // try {
 115:          // Thread.sleep(1000);
 116:          // } catch (InterruptedException e) {
 117:          // // TODO Auto-generated catch block
 118:          // e.printStackTrace();
 119:          // }
 120:      }
 121:   
 122:      private int[][] toIntMatrix() {
 123:          int[][] matrix = new int[size][size];
 124:          for (int i = 0; i < size; i++) {
 125:              for (int j = 0; j < size; j++) {
 126:                  matrix[i][j] = this.mPath[i][j].stepNum;
 127:              }
 128:          }
 129:          return matrix;
 130:      }
 131:   
 132:      /**
 133:       * 回退函数
 134:       * @return
 135:       */
 136:      private boolean goBack() {
 137:          if (this.stackL > 0) {
 138:              stackPoint[stackL].stepNum = NO_PASS;// 设置这个点没有走过
 139:              stackPoint[stackL].cleanDirsState();// 设置路径没有走过
 140:              this.lPoint = this.stackPoint[--stackL];// 退栈
 141:              step--;
 142:              return true;
 143:          }
 144:          return false;
 145:      }
 146:   
 147:      /**
 148:       * 获得下个最佳的点
 149:       * 
 150:       * @param lPoint
 151:       *            当前的点
 152:       * @return
 153:       */
 154:      public boolean goNext() {
 155:          MPoint[] ptsCanGo = new MPoint[8];// 临时存储那个方向可以去的点的序号
 156:          // 保存方向的位置
 157:          int j = 0;
 158:          for (int i = 0; i < 8; i++) {
 159:              if (isCanGo(lPoint, dirs[i])) {
 160:                  ptsCanGo[j] = mPath[lPoint.x + dirs[i].x][lPoint.y + dirs[i].y];// 获得可以走的点
 161:                  ptsCanGo[j].dirIndex = i;// 记录方向序号
 162:                  j++;
 163:              }
 164:          }
 165:          if (j == 0) return false;// 没有下一个点,向前走失败
 166:          int i = 0;
 167:          if (ptsCanGo[i].equals(stackPoint[stackL])) i++;// 防止第一个为来点
 168:          if (i > j) return false;// 表示唯一一个可以走的点为来点
 169:          MPoint[] pts = new MPoint[8];
 170:          // 判断点是否已经走过,没有走过的点保存在pts中
 171:          int n = 0;
 172:          int k = 0;
 173:          for (k = 0; k < j; k++) {
 174:              // 如果这个方向没有走过,并且你是来自这个方向的话
 175:              if (!lPoint.dirs[ptsCanGo[k].dirIndex]) {
 176:                  pts[n] = ptsCanGo[k];
 177:                  this.getPScore(pts[n]);// 给点打分
 178:                  n++;
 179:              }
 180:          }
 181:          if (n == 0) return false;// 所有点都走过了
 182:          MPoint mVPoint = pts[0];
 183:          for (int m = 1; m < n; m++) {
 184:              if (mVPoint.compareWithValue(pts[m]) == -1) {
 185:                  mVPoint = pts[m];
 186:              }
 187:          }
 188:          this.lPoint.dirs[mVPoint.dirIndex] = true;// 设置为已走过
 189:          this.lPoint = mVPoint;
 190:          lPoint.stepNum = step++;// 把步数存入点中
 191:          stackPoint[++stackL] = lPoint;// 已走过,压入栈中
 192:          return true;
 193:      }
 194:   
 195:      /**
 196:       * 给结点动态打分
 197:       * 
 198:       * @param mp
 199:       */
 200:      public void getPScore(MPoint mp) {
 201:          mp.firstValue =8;
 202:          mp.firstValue = this.canGoCount(mp);
 203:      }
 204:   
 205:      /**
 206:       * 给结点静态打分,某个结点分配下来就是静态的分数不会变的
 207:       * 
 208:       * @param mp
 209:       *            要打分的点
 210:       */
 211:   
 212:      public void getPSScore(MPoint mp) {
 213:          mp.seconedValue = Integer.MAX_VALUE;
 214:          
 215:          int lengthHalf = mPath.length / 2;
 216:          if (mp.x <= lengthHalf / 2) {
 217:              mp.seconedValue += mp.x;
 218:          } else {
 219:              mp.seconedValue += (mPath.length-mp.x);
 220:          }
 221:          if (mp.y <= lengthHalf / 2) {
 222:              mp.seconedValue += mp.y;
 223:          } else {
 224:              mp.seconedValue += (mPath.length-mp.y);
 225:          }
 226:  //        System.out.println(mp.seconedValue);
 227:      }
 228:   
 229:      /**
 230:       * 获得结点的出度
 231:       * 
 232:       * @param mp
 233:       *            结点
 234:       * @return
 235:       */
 236:      public int canGoCount(MPoint mp) {
 237:          int count = 0;
 238:          for (int i = 0; i < dirs.length; i++) {
 239:              if (isCanGo(mp, dirs[i])) {
 240:                  count++;
 241:              }
 242:          }
 243:          return count;
 244:      }
 245:   
 246:      /**
 247:       * 判断是否可以过去
 248:       * 
 249:       * @param mp
 250:       *            要判断的起点
 251:       * @param dir
 252:       *            代表方向的点
 253:       * @return
 254:       */
 255:      public boolean isCanGo(MPoint mp, Point dir) {
 256:          int x = mp.x + dir.x;
 257:          int y = mp.y + dir.y;
 258:          if (x < size && x >= 0 && y < size && y >= 0
 259:                  && mPath[x][y].stepNum == KnightTourPro.NO_PASS) { return true; }
 260:          return false;
 261:      }
 262:   
 263:      public static void main(String[] args) {
 264:          
 265:          
 266:          while (true) {
 267:              Scanner scan = new Scanner(System.in);
 268:              System.out.print("输入矩阵大小");
 269:              int size = scan.nextInt();
 270:              System.out.print("输入开始点\nx=");
 271:              int x = scan.nextInt();
 272:              System.out.print("y=");
 273:              int y = scan.nextInt();
 274:              KnightTourPro knight = new KnightTourPro(size);
 275:              final int[][] mPath = knight.doWalk(x - 1, y - 1);
 276:              if (mPath == null) {
 277:                  System.out.println("无解");
 278:              }
 279:              for (int[] i : mPath) {
 280:                  for (int j : i) {
 281:                      System.out.print(j + " ");
 282:                  }
 283:                  System.out.println();
 284:              }
 285:              new Thread(){
 286:                  @Override
 287:                  public void run(){
 288:                      ShowInfoWindow show = new ShowInfoWindow(mPath);
 289:                      show.setVisible(true);
 290:                      show.startAnimation();
 291:                  }
 292:              }.start();
 293:          }
 294:          
 295:   
 296:      }
 297:  }

MPoint

   1:  package nom.auggie.Knight;
   2:   
   3:  public class MPoint {
   4:   
   5:      /**
   6:       * 过来对应方向的序号
   7:       */
   8:      public int dirIndex = -1;
   9:      /**
  10:       * 对应点的步数
  11:       */
  12:      public int stepNum = 0;
  13:      /**
  14:       * 棋盘坐标x
  15:       */
  16:      public int x;
  17:   
  18:      /**
  19:       * 棋盘坐标y
  20:       */
  21:      public int y;
  22:   
  23:      /**
  24:       * 点的第一价值价值
  25:       */
  26:      public int firstValue=8;
  27:   
  28:      /**
  29:       * 第二价值
  30:       */
  31:      public int seconedValue=Integer.MAX_VALUE;
  32:   
  33:      /**
  34:       * 点的8个方向,顺时针数,走过的就设置为true;
  35:       */
  36:      public boolean[] dirs = new boolean[8];
  37:   
  38:      public MPoint puls(MPoint mPoint) {
  39:          return new MPoint(this.x + mPoint.x, this.y + mPoint.y);
  40:      }
  41:   
  42:      /**
  43:       * 比较价值,第一价值优先,
  44:       * 
  45:       * @param p
  46:       * @return 1 比p价值大,-1 比p价值小,0 价值相同
  47:       */
  48:      public int compareWithValue(MPoint p) {
  49:          if (p == null) return -1;
  50:          if (this.firstValue < p.firstValue) return 1;
  51:          if (this.firstValue == p.firstValue) {
  52:              if (this.seconedValue == p.seconedValue) return 0;
  53:              return this.seconedValue < p.seconedValue ? 1 : -1;
  54:          }
  55:          return -1;
  56:      }
  57:   
  58:      public void cleanDirsState() {
  59:          for (int i = 0; i < dirs.length; i++) {
  60:              dirs[i] = false;
  61:          }
  62:      }
  63:   
  64:      public MPoint(int x, int y, int fristValue, int seconedValue) {
  65:          this.x = x;
  66:          this.y = y;
  67:          this.firstValue = fristValue;
  68:          this.seconedValue = seconedValue;
  69:      }
  70:   
  71:      public MPoint() {
  72:      }
  73:   
  74:      public MPoint(int x, int y) {
  75:          this.x = x;
  76:          this.y = y;
  77:      }
  78:   
  79:      public MPoint(MPoint p) {
  80:          x = p.x;
  81:          y = p.y;
  82:          firstValue = p.firstValue;
  83:          seconedValue = p.seconedValue;
  84:      }
  85:   
  86:      /**
  87:       * 设定点的坐标
  88:       * 
  89:       * @param p
  90:       */
  91:      public void setMPoint(MPoint p) {
  92:          this.x = p.x;
  93:          this.y = p.y;
  94:      }
  95:   
  96:      @Override
  97:      public boolean equals(Object obj) {
  98:          if (obj == null) return false;
  99:          if (obj instanceof MPoint) {
 100:              MPoint mp = (MPoint) obj;
 101:              if (mp.x == this.x && mp.y == this.y) return true;
 102:          }
 103:          return false;
 104:      }
 105:  }

ShowInfoWindow

   1:  package nom.auggie.Knight.gui;
   2:   
   3:  import java.awt.Color;
   4:   
   5:  public class ShowInfoWindow extends JFrame {
   6:   
   7:      public static final int FREQUENCY = 2;
   8:      public static final int DELAY_TIME=50;
   9:      private Point[] pts;
  10:      private int[][] matrix;
  11:      private JPanel contentPane;
  12:      private JLabel[][] jLabels;
  13:      int[][] in = new int[50][50];
  14:      
  15:      /**
  16:       * Create the frame.
  17:       */
  18:  //    public ShowInfoWindow() {
  19:      public ShowInfoWindow(int[][] matrix) {
  20:          this.matrix = matrix;
  21:          setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
  22:          setBounds(100, 0, 1024, 768);
  23:          contentPane = new JPanel();
  24:          contentPane.setBorder(new EmptyBorder(5, 5, 5, 5));
  25:          setContentPane(contentPane);
  26:          GridLayout gLayout = new GridLayout(matrix.length,matrix.length);
  27:          int padding = 3;
  28:          gLayout.setHgap(padding);
  29:          gLayout.setVgap(padding);
  30:          contentPane.setLayout(gLayout);
  31:          jLabels = new JLabel[matrix.length][matrix.length];
  32:          for(int i=0;i<jLabels.length;i++){
  33:              for(int j=0;j<jLabels[i].length;j++){
  34:                  jLabels[i][j] = new JLabel();
  35:                  jLabels[i][j].setHorizontalAlignment(SwingConstants.CENTER);
  36:                  jLabels[i][j].setOpaque(true);
  37:                  contentPane.add(jLabels[i][j]);
  38:              }
  39:          }
  40:          pts = new Point[matrix.length*matrix.length];
  41:          this.analyseMatrix();
  42:      }
  43:      
  44:   
  45:      public void startAnimation(){
  46:          for(int i=0;i<pts.length;i++){
  47:              this.labelAnim(pts[i].x, pts[i].y);
  48:          }
  49:      }
  50:      
  51:   
  52:   
  53:      /**
  54:       * 渲染label
  55:       * @param indexX 对应的横坐标
  56:       * @param indexY 纵坐标
  57:       */
  58:      public void labelAnim(int indexX,int indexY){
  59:          JLabel aLab = jLabels[indexX][indexY];
  60:          aLab.setText(matrix[indexX][indexY]+"");
  61:          for(int i=0;i<FREQUENCY;i++){
  62:              aLab.setBackground(Color.BLUE);
  63:              aLab.updateUI();
  64:              try {
  65:                  Thread.sleep(DELAY_TIME);
  66:              } catch (InterruptedException e) {
  67:                  // TODO Auto-generated catch block
  68:                  e.printStackTrace();
  69:              }
  70:              aLab.setBackground(Color.red);
  71:              try {
  72:                  Thread.sleep(DELAY_TIME);
  73:              } catch (InterruptedException e) {
  74:                  // TODO Auto-generated catch block
  75:                  e.printStackTrace();
  76:              }
  77:          }
  78:      }
  79:      
  80:      /**
  81:       * 分析矩阵
  82:       */
  83:      public void analyseMatrix(){
  84:          for(int i=0;i<matrix.length;i++){
  85:              for(int j=0;j<matrix.length;j++){
  86:                  pts[matrix[i][j]-1] = new Point(i,j);
  87:              }
  88:          }
  89:      }
  90:   
  91:  }

运行效果图(8*8)

image

1. 自我评价及心得体会

程序实现的还不够好,理论上使用的启发式搜索的话应该可以跑到3000*3000的但是现在还只是能跑到26*26,和理论差太远了。还需努力。

心得体会:不用启发式的最多这是能搜到8*8而已,使用启发式立刻到26*26了差别很大。

原文地址:https://www.cnblogs.com/Jabba93/p/2919843.html