利用深度优先搜索解决八数码问题的进阶版本--十一数码。
十一数码是在12个格子中,存在一个空位及1-11个数字的游戏。
进阶版本不同于八数码上下左右移动的特性,可以向上、下、左、右、左上、左下、右上、右下,一共八个方向移动(不过当然也不能超出边界)。
代码先放出来,之后详细讲解。
import java.util.*;
import java.io.*;
public class miniProject {
public static void main(String[] args) {
Scanner scan = new Scanner(System.in);
String initState = scan.nextLine();
String goalState = scan.nextLine();
String currentState;
// Deep-First Search
//
DFS dfSearch = new DFS();
dfSearch.Init(initState, goalState);
dfSearch.Search();
dfSearch.findPath();
}
}
import java.util.*;
import java.io.*;
public class DFS {
// 思路:
// 图中存储当前节点及其父节点 用于抵达目标之后计算回溯路径
// close数组存储已访问过的节点 需要声明的大一些
// 图中存储的key值为每个状态+上一步是如何到达此状态的操作符'a'--'l'
// 代表将『0』移动至位置'*'
// 数据结构:
// Open 栈
// Close 数组
// Backtrack 图
private Stack openList = new Stack();
private Stack closeList = new Stack();
private HashMap backMap = new HashMap<>();
private Stack outputList = new Stack<>();
private Stack outputMoveList = new Stack<>();
private boolean findGoal = false;
private boolean canMove = false;
private String initState;
private String goalState;
private String currentState;
private String childState;
private String popState;
private String popModified;
private int numOfNodes = 0;
public DFS() {
}
public void Init(String initState, String goalState) {
openList.push(initState);
this.initState = initState;
this.goalState = goalState;
openList.push(initState);
backMap.put(initState, "0");
}
public void Search() {
// 比较起始节点,如果为目标节点 -->结束 找到目标
if(initState.equals(goalState)) {
// 若相同 -->结束 找到目标 跳出循环
findGoal = true;
System.out.println("Find the goal state!");
}
while (!openList.isEmpty() && !findGoal) {
// 如果栈为空
// 失败 -->"搜索因栈为空,失败了" 退出
// 将栈顶元素(记作结点N)从栈中取出
currentState = openList.pop();
// 若相同 跳出循环
if (currentState.equals(goalState)) {
// 若相同 -->结束 找到目标 跳出循环
findGoal = true;
//System.out.println("Find the goal state!");
break;
}
// 扩展结点N的所有结点,产生其全部的后继结点,并压入栈
// 应先产生UP-LEFT节点 压入栈!!!!
// 产生后继结点(检测后继结点是否合法)
for (int dir = 8; dir>=1; dir--){
// 尝试生成子节点
ChildState(dir);
// 无法移动 直接跳过
if (!canMove) {
continue;
}
// 检查后继结点是否存在于Open和Close中, 不存在的话就可以入Open栈
if (closeList.contains(childState) || openList.contains(childState)){
// 存在的话,嘛也不做
continue;
}
else {
openList.push(childState);
}
//System.out.println(childState);
// 存入回溯图
backMap.put(childState, currentState);
// (目前不执行此步骤)如果后继结点中有任一结点为目标结点,则求得解,否则转向步骤Ⅱ。
}
numOfNodes++;
// 子节点生成结束 存入close表
closeList.push(currentState);
}
if (findGoal)
System.out.println("Find the goal state!");
else
System.out.println("Could not find the goal state!");
if (openList.isEmpty())
System.out.println("ERROR! Open list is empty!");
}
// 寻找状态的后继结点
public void ChildState(int dir){
// 用x,y来存储0节点(空方快)的位置
int x = 0;
int y = 0;
// 字符串数组存储当前字符串状态
char[] currentArray = currentState.toCharArray();
// 寻找0点
for (int n = 0; n < 3; n++){
for (int m = 0; m < 4; m++){
if(currentState.substring(n * 4 + m, n * 4 + m + 1).equals("0")) {
x = n;
y = m;
}
}
}
// 从1-8 从UP->UP-RIGHT->RIGHT->.....->UP-LEFT
// 00 01 02 03 0 1 2 3
// 10 11 12 13 4 5 6 7
// 20 21 22 23 8 9 10 11
switch (dir){
case 1:
if (x == 0)
canMove = false;
else {
currentArray[x * 4 + y] = currentArray[x * 4 - 4 + y];
currentArray[x * 4 + y - 4] = '0';
canMove = true;
}
break;
case 2:
if (x == 0 || y == 3)
canMove = false;
else {
currentArray[x * 4 + y] = currentArray[x * 4 - 3 + y];
currentArray[x * 4 + y - 3] = '0';
canMove = true;
}
break;
case 3:
if (y == 3)
canMove = false;
else {
currentArray[x * 4 + y] = currentArray[x * 4 + 1 + y];
currentArray[x * 4 + y + 1] = '0';
canMove = true;
}
break;
case 4:
if (x == 2 || y == 3)
canMove = false;
else {
currentArray[x * 4 + y] = currentArray[x * 4 + 5 + y];
currentArray[x * 4 + y + 5] = '0';
canMove = true;
}
break;
case 5:
if (x == 2)
canMove = false;
else {
currentArray[x * 4 + y] = currentArray[x * 4 + 4 + y];
currentArray[x * 4 + y + 4] = '0';
canMove = true;
}
break;
case 6:
if (x == 2 || y == 0)
canMove = false;
else {
currentArray[x * 4 + y] = currentArray[x * 4 + 3 + y];
currentArray[x * 4 + y + 3] = '0';
canMove = true;
}
break;
case 7:
if (y == 0)
canMove = false;
else {
currentArray[x * 4 + y] = currentArray[x * 4 - 1 + y];
currentArray[x * 4 + y - 1] = '0';
canMove = true;
}
break;
case 8:
if (x == 0 || y == 0)
canMove = false;
else {
currentArray[x * 4 + y] = currentArray[x * 4 - 5 + y];
currentArray[x * 4 + y - 5] = '0';
canMove = true;
}
break;
}
childState = new String(currentArray);
}
// 转换输出格式
public void changePrintType() {
popModified = "[";
char[] popArray = popState.toCharArray();
for (int i = 0; i < popState.length() - 1; i++) {
popModified = popModified + popArray[i] + ", ";
}
popModified = popModified + popArray[popState.length() - 1];
popModified = popModified + "]";
}
public void findPath() {
// 从最后回溯
currentState = goalState;
while(!backMap.get(currentState).equals("0")) {
//System.out.println(currentState);
outputList.push(currentState);
currentState = backMap.get(currentState);
}
try {
File file = new File("/Users/sunny/Documents/miniProject1/DFS.txt");
PrintStream ps = new PrintStream(new FileOutputStream(file));
while (!outputList.isEmpty()) {
popState = outputList.pop();
// 寻找0点
for (int n = 0; n < 3; n++){
for (int m = 0; m < 4; m++){
if(popState.substring(n * 4 + m, n * 4 + m + 1).equals("0")) {
changePrintType();
switch (n * 4 + m + 1) {
case 1:
ps.println("a " + popModified);
break;
case 2:
ps.println("b " + popModified);
break;
case 3:
ps.println("c " + popModified);
break;
case 4:
ps.println("d " + popModified);
break;
case 5:
ps.println("e " + popModified);
break;
case 6:
ps.println("f " + popModified);
break;
case 7:
ps.println("g " + popModified);
break;
case 8:
ps.println("h " + popModified);
break;
case 9:
ps.println("i " + popModified);
break;
case 10:
ps.println("j " + popModified);
break;
case 11:
ps.println("k " + popModified);
break;
case 12:
ps.println("l " + popModified);
break;
}
}
}
}
}
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}