演算法-經典趣題-馬踏棋盤(又稱騎士周遊)
本文為joshua317原創文章,轉載請註明:轉載自joshua317部落格https://www.joshua317.com/article/97
一、問題
馬踏棋盤問題,又稱騎士漫步、,它是一個非常有趣的智力問題。馬踏棋盤問題的大意如下:
國際象棋的棋盤有8行8列共64個單元格,無論將馬放於棋盤的哪個單元格,都可讓馬踏遍棋盤的每個單元格。問馬應該怎麼走才可以踏遍棋盤的每個單元格?
二、分析
我們來分析一下馬踏棋盤問題。在國際象棋中,馬只能走“日”字形,但是馬位於不同的位置其可以走的方向有所區別:
當馬位於棋盤中間位置時,馬可以向8個方向跳動;
當馬位於棋盤的邊或角時,馬可以跳動的方向將少於8個。
另外,為了求解最少的走法,當馬所跳向的8個方向中的某一個或幾個方向已被馬走過,那麼馬也將跳至下一步要走的位置。可以使用遞迴的思想來解決馬踏棋盤問題。
我們可以使用遞迴的思想來解決馬踏棋盤問題,其操作步驟如下:
(1)從起始點開始向下一個可走的位置走一步。
(2)接著以該位置為起始,再向下一個可走的位置走一步。
(3)這樣不斷遞迴呼叫,直到走完64格單元格,就找到一個行走方案。
這裡需要注意的是,如果在行走過程中,某個位置向8個方向都沒有可走的點,則需要退回上一步,從上一個位置的另外一個可走位置繼續遞迴呼叫,直至找到一個行走方案。
三、程式設計
package com.joshua317; import java.util.Arrays; import java.util.Scanner; public class Main { public static void main(String[] args) { int i, j; Coordinate start = new Coordinate(0, 0); System.out.println("馬踏棋盤問題"); System.out.println("請輸入馬的一個其實位置(x,y)"); Scanner scanner = new Scanner(System.in); start.x = scanner.nextInt(); start.y = scanner.nextInt(); if (start.x < 1 || start.x > 8 || start.y < 1 || start.y > 8) { System.out.println("起始位置輸入有誤!END!!!"); } else { for (i = 0; i < 8; i++) { for (j = 0; j < 8; j++) { HorseChessBoard.chessBoard[i][j] = 0; } } start.x--; start.y--; HorseChessBoard.curstep = 1; HorseChessBoard.move(start); } } } class Coordinate { int x,y; public Coordinate (int a, int b) { x = a; y = b; } } class HorseChessBoard { static int[][] chessBoard = new int[8][8]; static int curstep; //馬可以走的8個方向 static Coordinate[] direction = { new Coordinate(-2, 1),new Coordinate(-1, 2),new Coordinate(1, 2),new Coordinate(2, 1), new Coordinate(2, -1),new Coordinate(1, -2),new Coordinate(-1, -2),new Coordinate(-2, -1) }; public static void move(Coordinate curpos) { Coordinate next = new Coordinate(0, 0); int i, j; //判斷是否越界 if (curpos.x < 0 || curpos.x > 7 || curpos.y < 0 || curpos.y > 7) { return; } //判斷是否走過 if (chessBoard[curpos.x][curpos.y] != 0) { return; } //儲存步數 chessBoard[curpos.x][curpos.y] = curstep; curstep++; //如果棋盤位置都走完 if (curstep > 64) { for (i = 0; i < 8; i++) { for (j = 0; j < 8; j++) { System.out.printf("%5d", chessBoard[i][j]); } System.out.println(""); } System.exit(0); } else { //8個可能的方向 for (i = 0; i < 8; i++) { next.x = curpos.x + direction[i].x; next.y = curpos.y + direction[i].y; if (next.x < 0 || next.x > 7 || next.y < 0 || next.y > 7) { } else { move(next); } } } //清除步數序號 chessBoard[curpos.x][curpos.y] = 0; curstep--; } }
執行該程式,輸入馬的一個起始位置(1,1),得到的結果如下圖所示。
如果輸入馬的另外一個起始位置(8,8),得到的結果如下圖所示。
四、擴充套件
馬踏棋盤是經典的程式設計問題之一,主要的解決方案有兩種:
一種是基於深度優先搜尋的方法,另一種是基於貪婪演算法的方法。
第一種基於深度優先搜尋的方法是比較常用的演算法,深度優先搜尋演算法也是資料結構中的經典演算法之一,主要是採用遞迴的思想,一級一級的尋找,最後找到合適的解。
而基於貪婪的演算法則是依據貪婪演算法的思想設定一種標準,然後依據標準進行選擇,從而得到解,但是他不一定能夠得到最優解。
深度優先搜尋屬於圖演算法的一種,英文縮寫為DFS即Depth First Search.其過程簡要來說是對每一個可能的分支路徑深入到不能再深入為止,而且每個節點只能訪問一次.
貪心演算法(又稱貪婪演算法)是指,在對問題求解時,總是做出在當前看來是最好的選擇。也就是說,不從整體最優上加以考慮,他所做出的僅是在某種意義上的區域性最優解。貪心演算法不是對所有問題都能得到整體最優解,但對範圍相當廣泛的許多問題他能產生整體最優解或者是整體最優解的近似解。
基於深度優先搜尋的演算法就是依據當前點找到下一個可能的點,然後對這個點進行深度優先搜尋,然後依次遞迴,當出現條件不滿足時,退回來,採用其他的路勁進行搜尋,最後肯定能夠得到對應的結果。
五、通過貪心演算法實現
採用貪心演算法,對路徑有目的地篩選,儘量選擇出口少的路先走,也就是對當前點的下一個落腳點(可能是8個)進行排序,優先走可走的路最少的那個點,使得走法較好。通俗來講,就是先預判下一個可能落腳點的出口數,出口數最少的先走掉。