[Algo] Eight Puzzle Game 九宫格游戏 - SegmentFault
九宫格游戏,给定最终状态如下,再给出一个初始状态,0代表空位,相邻的格子中的方块可以和0互换位置,求初始状态到最终状态的最小步数。
Read full article from [Algo] Eight Puzzle Game 九宫格游戏 - SegmentFault
九宫格游戏,给定最终状态如下,再给出一个初始状态,0代表空位,相邻的格子中的方块可以和0互换位置,求初始状态到最终状态的最小步数。
1 2 3
4 5 6
7 8 0
A*搜索
典型的A*算法应用,我们将棋盘的状态抽象为类似
“123456780”
的字符串,其中0代表空位,然后每3位代表一行,这时候我们从起始状态开始搜索,搜索中使用一个优先队列存放边缘节点,类似于广度优先搜索,但我们每次取出来的都是函数值最小的节点。这里函数值f(n) = g(n) + h(n)
,其中g(n)是到达当前节点的开销,这里也就是步数,而h(n)是指我们对这个节点计算的heuristic值,这题我们有两种可选方法计算heuristic值:- 判断错位的格子数量,比如拍
213456780
中,2和1是错位的,所以h值是2 - 判断每个格子中数字和其目标状态的位置的曼哈顿距离之和
这里为了实现简单,我们采取第一种,但实际上第二种heuristic优化的时间可以达到一个数量级之上,因为这里第二种总是dominant第一种。
public static final String FINAL_STATE = "123456780";
public class Node{
String state;
Node parent;// 父亲节点,便于回溯
int action;// 表示父亲节点到达该节点所做出的action
int cost;// cost值,这里是步数
int heuristic; // heuristic值
public Node(String s, Node parent, int a, int c, int h){
this.state = s;
this.parent = parent;
this.action = a;
this.cost = c;
this.heuristic = h;
}
}
public int solvePuzzle(String initial){
HashSet<String> visited = new HashSet<String>();
int initH = getHeuristic(initial);
Node root = new Node(initial, null, 0, 0, initH);
PriorityQueue<Node> pq = new PriorityQueue<Node>(11, new Comparator<Node>(){
public int compare(Node n1, Node n2){
return (n1.cost + n1.heuristic) - (n2.cost + n2.heuristic);
}
});
pq.offer(root);
while(!pq.isEmpty()){
Node curr = pq.poll();
if(curr.state.equals(FINAL_STATE)){
return curr.cost;
}
getAllChildrenStates(curr, pq, visited);
}
return -1;
}
public void getAllChildrenStates(Node curr, PriorityQueue<Node> pq, HashSet<String> visited){
int pos = curr.state.indexOf('0');
if(pos > 2){
String child = move(curr.state, 1);//DOWN
if(!visited.contains(child)){
int childH = getHeuristic(child);
pq.offer(new Node(child, curr, 1, curr.cost + 1, childH));
}
}
if(pos < 6){
String child = move(curr.state, 2);//UP
if(!visited.contains(child)){
int childH = getHeuristic(child);
pq.offer(new Node(child, curr, 2, curr.cost + 1, childH));
}
}
if(pos != 0 && pos != 3 && pos != 6){
String child = move(curr.state, 3);//RIGHT
if(!visited.contains(child)){
int childH = getHeuristic(child);
pq.offer(new Node(child, curr, 3, curr.cost + 1, childH));
}
}
if(pos != 2 && pos != 5 && pos != 8){
String child = move(curr.state, 4);//LEFT
if(!visited.contains(child)){
int childH = getHeuristic(child);
pq.offer(new Node(child, curr, 4, curr.cost + 1, childH));
}
}
}
public int getHeuristic(String state){
int cnt = 0;
for(int i = 0; i < 9; i++){
if(state.charAt(i) == FINAL_STATE.charAt(i)){
cnt++;
}
}
return cnt;
}
public String move(String original, int direction){
int pos = original.indexOf('0');
StringBuilder sb = new StringBuilder(original);
switch(direction){
case 1:
sb.setCharAt(pos, sb.charAt(pos - 3));
sb.setCharAt(pos - 3, '0');
break;
case 2:
sb.setCharAt(pos, sb.charAt(pos + 3));
sb.setCharAt(pos + 3, '0');
break;
case 3:
sb.setCharAt(pos, sb.charAt(pos - 1));
sb.setCharAt(pos - 1, '0');
break;
case 4:
sb.setCharAt(pos, sb.charAt(pos + 1));
sb.setCharAt(pos + 1, '0');
break;
}
return sb.toString();
}