马周游问题(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)
1. 自我评价及心得体会
程序实现的还不够好,理论上使用的启发式搜索的话应该可以跑到3000*3000的但是现在还只是能跑到26*26,和理论差太远了。还需努力。
心得体会:不用启发式的最多这是能搜到8*8而已,使用启发式立刻到26*26了差别很大。