题目原文:http://coursera.cs.princeton.edu/algs4/assignments/8puzzle.html

题目要求:设计一个程序解决8 puzzle问题以及该问题的推广,例如8-puzzle是3*3,程序要能解决n*n的同类问题(2 ≤ n < 128)

典型的8 puzzle如下:

算法设计参照A*搜索算法,即使不了解A*搜索算法,题目也已经将解法解释的很具体了。

Best-first search:设计参照A* 搜索算法。定义一个 search node类,包含board,从初始状态到达当前board状态的移动权重moves,和previous search node。

  1. 插入初始的search node,其board设为初始board,moves设为0,previous search node设置为0
  2. 将初始化的search node置于MinPQ类型的优先级队列中
  3. 删除优先级队列中的min节点,再将该节点的邻居节点放入优先级队列中。
  4. 重复2和3操作直至从优先级队列中删除的min节点是目标board

A* 搜索算法的优先级判定依据是f(n) = g(n) + h(n),g(n)是从初始节点到达当前节点的代价,h(n)是当前节点到目标节点的预估代价。

在本题中search node中的moves就是g(n),而关于h(n)题目给出了两种候选:

Hamming priority function:  处于错误位置的block的个数(空白处不算block)

Manhatten priority function: 处于错误位置的block距离其各自目标位置的横向和纵向距离之和

h(n)采用这两者均可,根据题目中的图示,显然发现题目推荐采用manhatten方法。

至此优先级队列中的优先级判断依据就是当前search node的moves+manhatten value

A critical optimization: 上述Best-first search中可能会存在刚出队列的节点又被当成其邻居节点的邻居而被放回优先级队列的情况,这种情况会造成很大的性能损失。为了阻止这种情况的发生,可以在Best-first search的第3步“将该节点的邻居节点放入优先级队列”时比较下这个邻居节点的board是否与本节点的board相同。

例如此时{{8,1,3},{4,0,2},{7,6,5}}就不应该放入优先级队列中。

A second optimization: 建议在search node的构造函数中计算其manhattan值,也就是在search node的构造函数中确定其优先级。

Detecting unsolvable puzzles:如果一个board是不可解的,那么随便在该board中选择一对block互换位置,就能将其变为可解的。为此采用同时对board和其互换了一对block的twindboard进行求解,如果board先实现目标解,那么其就是可解的,相反,如果twinboard先实现目标节,那么该board就不可解。

 import java.util.ArrayList;
/**
* @author evasean www.cnblogs.com/evasean/
*/
public class Board {
private static final int BLANK = 0;
private final int n;
private int[][] blocks; public Board(int[][] inBlocks) {
// construct a board from an n-by-n array of blocks
// (where blocks[i][j] = block in row i, column j)
n = inBlocks.length;
blocks = new int[n][n];
copy(blocks, inBlocks);
} private void copy(int[][] toBlocks, int[][] fromBlocks) {
for (int row = 0; row < n; row++)
for (int col = 0; col < n; col++)
toBlocks[row][col] = fromBlocks[row][col];
} public int dimension() {
// board dimension n
return n;
} private int getRow(int value) {
return (value - 1) / n;
} private int getCol(int value) {
return (value - 1) % n;
} private int getValue(int row, int col) {
return row * n + col + 1;
} public int hamming() {
// number of blocks out of place
int hamming = 0;
for (int row = 0; row < n; row++)
for (int col = 0; col < n; col++)
if (blocks[row][col] != BLANK && blocks[row][col] != getValue(row, col))
hamming++;
return hamming;
} public int manhattan() {
// sum of Manhattan distances between blocks and goal
int manhattan = 0;
for (int row = 0; row < n; row++)
for (int col = 0; col < n; col++)
if (blocks[row][col] != BLANK && blocks[row][col] != getValue(row, col))
manhattan += Math.abs(getRow(blocks[row][col]) - row) + Math.abs(getCol(blocks[row][col]) - col);
return manhattan;
} public boolean isGoal() {
// is this board the goal board?
for (int row = 0; row < n; row++)
for (int col = 0; col < n; col++)
if (blocks[row][col] != BLANK && blocks[row][col] != getValue(row, col))
return false;
return true;
} public Board twin() {
// a board that is obtained by exchanging any pair of blocks
Board twinBoard = new Board(blocks);
int firRow = 0;
int firCol = 0;
if (blocks[firRow][firCol] == BLANK)
firCol++;
for (int row = 0; row < n; row++) {
for (int col = 0; col < n; col++) {
if (blocks[row][col] != blocks[firRow][firCol] && blocks[row][col] != BLANK) {
twinBoard.swap(firRow, firCol, row, col);
return twinBoard;
}
}
}
return twinBoard;
} private void swap(int vRow, int vCol, int wRow, int wCol) {
int t = blocks[vRow][vCol];
blocks[vRow][vCol] = blocks[wRow][wCol];
blocks[wRow][wCol] = t;
} public boolean equals(Object y) {
// does this board equal y?
if (y == null)
return false;
if (y == this)
return true;
if (y.getClass().isInstance(this)) {
Board yb = (Board) y;
if (yb.n != this.n)
return false;
else {
for (int row = 0; row < n; row++)
for (int col = 0; col < n; col++)
if (yb.blocks[row][col] != blocks[row][col])
return false;
return true;
}
} else
return false;
} public Iterable<Board> neighbors() {
// all neighboring boards
ArrayList<Board> neighbors = new ArrayList<Board>();
for (int row = 0; row < n; row++) {
for (int col = 0; col < n; col++) {
if (blocks[row][col] == BLANK) {
// 空白的位置分别与上下左右的元素交换一次位置就得到一个邻居board
// 与上方元素互换
if (row > 0) {
Board neighborT = new Board(blocks);
neighborT.swap(row, col, row - 1, col);
neighbors.add(neighborT);
}
// 与下方元素互换
if (row < n - 1) {
Board neighborB = new Board(blocks);
neighborB.swap(row, col, row + 1, col);
neighbors.add(neighborB);
}
// 与左边元素互换
if (col > 0) {
Board neighborL = new Board(blocks);
neighborL.swap(row, col, row, col - 1);
neighbors.add(neighborL);
}
// 与右边元素互换
if (col < n - 1) {
Board neighborR = new Board(blocks);
neighborR.swap(row, col, row, col + 1);
neighbors.add(neighborR);
}
}
}
}
return neighbors;
} public String toString() {
// string representation of this board (in the output format specified
// below)
StringBuilder sb = new StringBuilder();
sb.append(n + "\n");
for (int row = 0; row < n; row++) {
for (int col = 0; col < n; col++) {
//本来考虑到n<128时元素可能会很大,设置的是%6d,但是提交时不满足校验规则
//校验规则要求必须是%2d,很奇怪的校验
sb.append(String.format("%2d ", blocks[row][col]));
}
sb.append("\n");
}
return sb.toString();
} public static void main(String[] args) {
// unit tests (not graded)
// int[][] test = { { 0, 1}, {2,3 }};
// Board b = new Board(test);
// System.out.println(b);
// System.out.println(b.hamming());
// System.out.println(b.manhattan());
}
}
 import edu.princeton.cs.algs4.In;
import edu.princeton.cs.algs4.MinPQ;
import edu.princeton.cs.algs4.Stack;
import edu.princeton.cs.algs4.StdOut;
/**
* @author evasean www.cnblogs.com/evasean/
*/
public class Solver { private SearchNode currentNode;
private SearchNode twincurrentNode;
private Stack<Board> solution; private class SearchNode implements Comparable<SearchNode>{
public Board board;
public int moves;
public SearchNode preSearchNode; public final int priority; public SearchNode(Board inboard, SearchNode inPreSearchNode){
board = inboard;
preSearchNode = inPreSearchNode;
if(inPreSearchNode == null) moves = 0;
else moves = inPreSearchNode.moves + 1;
priority = moves + board.manhattan();
} @Override
public int compareTo(SearchNode o) {
return Integer.compare(this.priority, o.priority);
}
} public Solver(Board initial) {
// find a solution to the initial board (using the A* algorithm)
if(initial == null)
throw new IllegalArgumentException("Constructor argument Board is null!");
currentNode = new SearchNode(initial,null);
twincurrentNode = new SearchNode(initial.twin(),null);
MinPQ<SearchNode> priorityQueue = new MinPQ<SearchNode>();
MinPQ<SearchNode> twinPriorityQueue = new MinPQ<SearchNode>();
priorityQueue.insert(currentNode);
twinPriorityQueue.insert(twincurrentNode);
while(true){
currentNode = priorityQueue.delMin();
if(currentNode.board.isGoal()) break;
putNeighBorsIntoPQ(currentNode,priorityQueue); twincurrentNode = twinPriorityQueue.delMin();
if(twincurrentNode.board.isGoal()) break;
putNeighBorsIntoPQ(twincurrentNode,twinPriorityQueue);
}
} private void putNeighBorsIntoPQ(SearchNode searchNode, MinPQ<SearchNode> pq){
Iterable<Board> neighbors = searchNode.board.neighbors();
for(Board neighbor : neighbors){
//只有在当前搜索节点的邻居们的borad不与当前节点的preSearchNode的borad相同
//才将该邻居放入优先队列 if(searchNode.preSearchNode==null || !neighbor.equals(searchNode.preSearchNode.board))
pq.insert(new SearchNode(neighbor,searchNode));
}
} public boolean isSolvable() {
// is the initial board solvable?
return currentNode.board.isGoal();
} public int moves() {
// min number of moves to solve initial board; -1 if unsolvable
if(currentNode.board.isGoal())
return currentNode.moves;
else
return -1;
} public Iterable<Board> solution() {
// sequence of boards in a shortest solution; null if unsolvable
if(currentNode.board.isGoal()){
solution = new Stack<Board>();
SearchNode node = currentNode;
while(node != null){
solution.push(node.board);
node = node.preSearchNode;
}
return solution;
}else
return null;
} public static void main(String[] args) {
// solve a slider puzzle (given below)
// create initial board from file
// In in = new In(args[0]);
In in = new In("8puzzle/puzzle3x3-unsolvable.txt"); //本地测试之用
int n = in.readInt();
int[][] blocks = new int[n][n];
for (int i = 0; i < n; i++)
for (int j = 0; j < n; j++)
blocks[i][j] = in.readInt();
Board initial = new Board(blocks); // solve the puzzle
Solver solver = new Solver(initial); // print solution to standard output
if (!solver.isSolvable())
StdOut.println("No solution possible");
else {
StdOut.println("Minimum number of moves = " + solver.moves());
for (Board board : solver.solution())
StdOut.println(board);
}
}
}

Coursera Algorithms Programming Assignment 4: 8 Puzzle (100分)的更多相关文章

  1. Coursera Algorithms Programming Assignment 1: Percolation(100分)

    题目来源http://coursera.cs.princeton.edu/algs4/assignments/percolation.html 作业分为两部分:建立模型和仿真实验. 最关键的部分就是建 ...

  2. Coursera Algorithms Programming Assignment 5: Kd-Trees (98分)

    题目地址:http://coursera.cs.princeton.edu/algs4/assignments/kdtree.html 分析: Brute-force implementation. ...

  3. Coursera Algorithms Programming Assignment 2: Deque and Randomized Queue (100分)

    作业原文:http://coursera.cs.princeton.edu/algs4/assignments/queues.html 这次作业与第一周作业相比,稍微简单一些.有三个编程练习:双端队列 ...

  4. Coursera Algorithms Programming Assignment 3: Pattern Recognition (100分)

    题目原文详见http://coursera.cs.princeton.edu/algs4/assignments/collinear.html 程序的主要目的是寻找n个points中的line seg ...

  5. Algorithms : Programming Assignment 3: Pattern Recognition

    Programming Assignment 3: Pattern Recognition 1.题目重述 原题目:Programming Assignment 3: Pattern Recogniti ...

  6. Programming Assignment 4: 8 Puzzle

    The Problem. 求解8数码问题.用最少的移动次数能使8数码还原. Best-first search.使用A*算法来解决,我们定义一个Seach Node,它是当前搜索局面的一种状态,记录了 ...

  7. Algorithms: Design and Analysis, Part 1 - Programming Assignment #1

    自我总结: 1.编程的思维不够,虽然分析有哪些需要的函数,但是不能比较好的汇总整合 2.写代码能力,容易挫败感,经常有bug,很烦心,耐心不够好 题目: In this programming ass ...

  8. Coursera课程 Programming Languages, Part A 总结

    Coursera CSE341: Programming Languages 感谢华盛顿大学 Dan Grossman 老师 以及 Coursera . 碎言碎语 这只是 Programming La ...

  9. 课程一(Neural Networks and Deep Learning),第三周(Shallow neural networks)—— 3.Programming Assignment : Planar data classification with a hidden layer

    Planar data classification with a hidden layer Welcome to the second programming exercise of the dee ...

随机推荐

  1. 打造完美的ImageLoader——LruCache+DiskLruCache

    做android应用少不了要和网络打交道,在我刚开始学android的时候总是处理不好网络图片的加载,尤其是图片乱跳的问题,后来发现了各种缓存图片的方法:本地缓存.软引用.LruCache.... 我 ...

  2. 四角递推(CF Working out,动态规划递推)

    题目:假如有A,B两个人,在一个m*n的矩阵,然后A在(1,1),B在(m,1),A要走到(m,n),B要走到(1,n),两人走的过程中可以捡起格子上的数字,而且两人速度不一样,可以同时到一个点(哪怕 ...

  3. unity问题笔记

    拖放在预制体中的图片等资源,他们的加载需要我们控制吗?我觉得不需要控制,但是如果按照现在的这种方式保存资源到非标准的resources文件下,那怎么加载?ulua的规则是这样查找资源的吗?猜想:客户端 ...

  4. Go:字符串操作

    Package strings:https://golang.google.cn/pkg/strings/ package main import ( "fmt" "st ...

  5. 7-26 Windows消息队列

    7-26 Windows消息队列(25 分) 消息队列是Windows系统的基础.对于每个进程,系统维护一个消息队列.如果在进程中有特定事件发生,如点击鼠标.文字改变等,系统将把这个消息加到队列当中. ...

  6. 【Codeforces 1030D】Vasya and Triangle

    [链接] 我是链接,点我呀:) [题意] 题意 [题解] 参考这篇题解:https://blog.csdn.net/mitsuha_/article/details/82825862 为什么可以保证m ...

  7. Leetcode 87.扰乱字符串

    扰乱字符串 给定一个字符串 s1,我们可以把它递归地分割成两个非空子字符串,从而将其表示为二叉树. 下图是字符串 s1 = "great" 的一种可能的表示形式. 在扰乱这个字符串 ...

  8. A^B Mod C

    基准时间限制:1 秒 空间限制:131072 KB 分值: 0 难度:基础题 给出3个正整数A B C,求A^B Mod C.   例如,3 5 8,3^5 Mod 8 = 3. Input 3个正整 ...

  9. [bzoj 1047][HAOI2007]理想正方形(单调队列)

    题目:http://www.lydsy.com/JudgeOnline/problem.php?id=1047 分析: 第一感觉二维线段树当然没问题但是挺慢的. 注意到要求的正方形形中的最大最小边长是 ...

  10. Ext.data.JsonStore的使用

    最近在维护一个Ext.js写的贷前服务系统,Ext.data.JsonStore相当于前台页面的一个数据仓库,负责保存后台传过来的Json数据,具体用法如下: var store12=new Ext. ...