八數碼破解(bfs+hash)
阿新 • • 發佈:2019-01-29
問題描述:編號為1-8的8格正方形滑塊被擺成3行3列 (有一個格子留空),每次可以把與空格相鄰的滑塊移到空格中,而它原來的位置就成為了新的空格。給定初始局面和目標局面(用0表示空格),計算出最少步數並輸出路徑。
思路:我們可以假定每一種情況(0-8的排列)都為一種狀態,,那麼題目就變成從初始狀態(初始局面)到達最終狀態(最終局面)的最少步數,那麼通過bfs就可以解決最少步數問題,通過嘗試空格的每一種移動,得到下一步的狀態,判斷該狀態是否已經到達過(Hash判重),若已經到達過則剪枝,直到得到最終狀態!
import java.util.*;
public class 八數碼 {
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
int[][] begin = new int[3][3];// 初始局面
int[][] target = new int[3][3];// 最終局面
for (int i = 0; i < 3; i++)
for (int j = 0; j < 3; j++)
begin[i][j] = in.nextInt();
for (int i = 0; i < 3; i++)
for (int j = 0; j < 3; j++)
target[i][j] = in.nextInt();
bfs(begin, target);
}
private static void bfs(int[][] arr, int[][] target) {
// 獲得初始局面的空格位置
int space_x = 0;
int space_y = 0;
for (int i = 0 ; i < 3; i++)
for (int j = 0; j < 3; j++) {
if (arr[i][j] == 0) {
space_x = i;
space_y = j;
}
}
int[][] dir = new int[][] { { 1, 0 }, { 0, 1 }, { -1, 0 }, { 0, -1 } };
char[] c = new char[] { '下', '右', '上', '左' };
LinkedList<Status> queue = new LinkedList<Status>();
// 通過hashset可以對狀態去重
HashSet<Status> set = new HashSet<Status>();
Status target_status = new Status(target, -1, -1, -1);// 構建目標狀態
Status begin_status = new Status(arr, 0, space_x, space_y);// 構建初始狀態
begin_status.path = "";
queue.add(begin_status);
set.add(begin_status);
while (!queue.isEmpty()) {
Status current_status = queue.poll();// 得到當前狀態
// 若當前狀態與目標狀態相等則得到最少步數及其路徑
if (current_status.equals(target_status)) {
System.out.println(current_status);
return;
}
for (int i = 0; i < 4; i++) {
int lx = current_status.space_x + dir[i][0];
int ly = current_status.space_y + dir[i][1];
String path = current_status.path + c[i];
if (lx >= 0 && lx < 3 && ly >= 0 && ly < 3) {
// 移動空格,通過交換的方式
int temp = current_status.arr[current_status.space_x][current_status.space_y];
current_status.arr[current_status.space_x][current_status.space_y] = current_status.arr[lx][ly];
current_status.arr[lx][ly] = temp;
Status ss = new Status(current_status.arr, current_status.step + 1, lx, ly);
ss.path = path;
if (set.add(ss))
queue.add(ss);
// 狀態改變了,要恢復原來狀態
temp = current_status.arr[current_status.space_x][current_status.space_y];
current_status.arr[current_status.space_x][current_status.space_y] = current_status.arr[lx][ly];
current_status.arr[lx][ly] = temp;
}
}
}
System.out.println("無法到達");
}
}
// 狀體類,儲存當前狀態(0-8的一種排列),到達當前狀態的最短路徑和步數
class Status {
int[][] arr = new int[3][3];
int step; // 步數
int space_x; // 空格0的x座標
int space_y; // 空格0的y座標
String path;
Status(int[][] arr, int step, int locationx, int locationy) {
for (int i = 0; i < 3; i++)
for (int j = 0; j < 3; j++)
this.arr[i][j] = arr[i][j];
this.step = step;
this.space_x = locationx;
this.space_y = locationy;
}
@Override
public int hashCode() {
int hash = 0;
for (int i = 0; i < 3; i++)
for (int j = 0; j < 3; j++)
hash = hash * 10 + arr[i][j];
return hash;
}
@Override
public boolean equals(Object o) {
return hashCode() == o.hashCode();
}
@Override
public String toString() {
return "最少步數" + step + "\n空格移動路徑為:" + path;
}
}
Input:
2 6 4 1 3 7 0 5 8
8 1 5 7 3 6 4 0 2
Output:
最少步數31
空格移動路徑為:右右上上左下左上右下左下右右上左左上右下左下右右上上左左下下右