Coursera普林斯顿大学算法Week4:8 Puzzle 解迷

10 篇文章 0 订阅
9 篇文章 0 订阅

任务链接:http://coursera.cs.princeton.edu/algs4/assignments/8puzzle.html

本次任务难点在于构建合理的内部变量,尤其是Solver类中的变量,然后根据提示一步步解出来就行了。

Solver类中的变量如下:

        private SearchNode currentNode;
	private SearchNode currentTwinNode;
	private Stack<Board> stackBoard;

其中stackBoard利用堆栈存储处理的结果集。该结果集是根据最后的目标结果依次往前寻找其前驱节点的,所以利用堆栈正好可以将将结果集的顺序倒过来。

SearchNode为寻找节点类。该内部类包含如下元素:

private final Board board;   // 当前棋盘情况
private final int moves;    // 当前棋盘移动步数
private SearchNode preSearcchNode = null;  // 当前棋盘前驱
private final int priority;   // 当前状态优先级

 

currentNode为原始面板当前寻找节点,currentTwinNode为交换面板当前寻找节点。根据数学可证,若原始面板无解则交换面板 一定有解。其交换方法如下:

 

 public Board twin()                    // 通过交换任何一对块而获得的板。
    {
    	int i1 = 0, j1 = 0, i2 = 1, j2 = 1;
    	if (blocks[i1][j1] == BLANK) 
    	{
    		i1 = 1;
    		j1 = 0;
    	}
    	if (blocks[i2][j2] == BLANK)
    	{
    		i2 = 1;
    		j2 = 0;
    	}
    	
    	Board newBoard = swap(i1, j1, i2, j2);
    	
    	return newBoard;
    }

其中swap是交换数组中a[i1][j1]和a[i2][j2]的值,该交换是全域的,所以应该先将构造一个交换数组,在将原数组中的值拷贝过来,然后在进行交换操作。

另外,本文中的代码在存储中还未达标,需要改进。

import java.util.ArrayList;

public class Board {
	private final int [][]blocks;
	private static final int BLANK = 0;
	private final int N;
	public Board(int[][] blocks)           // 创建n*n的棋盘
	{
		if (blocks == null) 
			throw new java.lang.IllegalArgumentException("the blocks is null");
		
		N = blocks.length;
		
		this.blocks = new int[N][N];
		for (int i = 0; i < N; i++) {
			for (int j = 0; j < N; j++) {
				this.blocks[i][j] = blocks[i][j];
			}
		}
	}
	
    public int dimension()                 // 棋盘维度n
    {
    	return N;  // 返回列数
    }
    
    private int getIndex(int i, int j) // 二维变一维坐标
    {
		return i * N + j + 1;
	}
   
    private int getRow(int value) // 在目标结果中,值所对应的行数
    {
		return (value - 1) / N;
	}
     private int getCol(int value) // 在目标结果中,值所对应的列数
    {
    	return (value -1) % N;
	}
    public int hamming()                   // hamming优先级=错的位置个数+已移动次数
    {
    	int wrongNum = 0;
    	for (int i = 0; i < N; i++) 
			for (int j = 0; j < N; j++) 
				if (blocks[i][j] != BLANK && blocks[i][j] != getIndex(i, j)) // blocks[i][j]位置上的元素放错
					wrongNum++;
		return wrongNum;
    }
    
    public int manhattan()                 // manhattan优先级=距离目标距离+已移动次数
    {
    	int wrongNum = 0;
    	for (int i = 0; i < N; i++) 
    	{
    		for (int j = 0; j < N; j++) 
				if (blocks[i][j] != BLANK && blocks[i][j] != getIndex(i, j))  // blocks[i][j]位置上的元素放错
				{
					int righti = getRow(blocks[i][j]); // blocks[i][j]位置上的元素正确的i坐标
					int rightj = getCol(blocks[i][j]); // blocks[i][j]位置上的元素正确的j坐标
					wrongNum = wrongNum + Math.abs(righti - i) + Math.abs(rightj -j);
				}
    	}
    	return wrongNum;
    }
    
    public boolean isGoal()                // 棋盘是否为目标位置
    {
    	for (int i = 0; i < N; i++) 
			for (int j = 0; j < N; j++) 
				if (blocks[i][j] != BLANK && blocks[i][j] != getIndex(i, j))  // blocks[i][j]位置上的元素放错
					return false;
    	return true;
    }
    
    private int[][] copy() // 拷贝棋盘元素
    {
    	int [][]newblocks = new int[N][N];   
    	for (int i1 = 0; i1 < N; i1++)
			for (int j1 = 0; j1 < N; j1++) 
				newblocks[i1][j1] = this.blocks[i1][j1];
    	return newblocks;
	}
    
    private Board swap(int i1, int j1, int i2, int j2) // 交换两个棋盘元素位置
    {
    	int [][]newblocks = copy();
    	int temp = newblocks[i1][j1];
    	newblocks[i1][j1] = newblocks[i2][j2];
    	newblocks[i2][j2] = temp;
    	return new Board(newblocks);
    }
    
    public Board twin()                    // 通过交换任何一对块而获得的板。
    {
    	int i1 = 0, j1 = 0, i2 = 1, j2 = 1;
    	if (blocks[i1][j1] == BLANK) 
    	{
    		i1 = 1;
    		j1 = 0;
    	}
    	if (blocks[i2][j2] == BLANK)
    	{
    		i2 = 1;
    		j2 = 0;
    	}
    	
    	Board newBoard = swap(i1, j1, i2, j2);
    	
    	return newBoard;
    }
    public boolean equals(Object y)        // 判断两个棋盘是否相等
    {
    	if (y == null)  
    		return false;
    	if (y == this) 
    		return true;
    	
    	if (y.getClass().isInstance(this)) // y和this属于同一类型  
    	{
    		Board boardY = (Board) y;
    		if (boardY.N != this.N)
    			return false;
    		for (int i = 0; i < N; i++) 
    			for (int j = 0; j < N; j++) 
    				if (this.blocks[i][j] != boardY.blocks[i][j]) 
    					return false;
    	}
    	else // y和this不属于同一类型 
    	{
			return false;
		}
    	
    	return true;
    }
    public Iterable<Board> neighbors()     // 所有的邻居棋盘
    {
    	ArrayList<Board> boards = new ArrayList<>();
    	
    	for (int i = 0; i < N; i++) 
    	{
    		for (int j = 0; j < N; j++)
    		{
    			if (blocks[i][j] == BLANK)  // 找到空格位置 
    			{
					// 上
    				if (i > 0) 
    				{
    					Board upBoard = swap(i, j, i-1, j);
						boards.add(upBoard);
					}
    				
    				// 下
    				if (i < N-1) 
    				{
    					Board lowBoard = swap(i, j, i+1, j);
    					boards.add(lowBoard);
					}
    				
    				// 左
    				if (j > 0) 
    				{
    					Board leftBoard = swap(i, j, i, j-1);
    					boards.add(leftBoard);
					}
    				
    				// 右
    				if (j < N-1) 
    				{
    					Board rightBoard = swap(i, j, i, j+1);
    					boards.add(rightBoard);
					}
				}
    			
			}
    		
		}
		return boards;
    }
    public String toString()               // 此板的字符串表示形式(以下面指定的输出格式)
    {
    	StringBuilder sBuilder = new StringBuilder();
    	sBuilder.append(N + "\n");
    	for (int i = 0; i < N; i++) 
    	{
    		for (int j = 0; j < N; j++) 
    		{
    			sBuilder.append(blocks[i][j] +"\t");
			}
    		sBuilder.append("\n");
		}
    	String string = sBuilder.toString();
    	return string;
    }
    public static void main(String[] args) // unit tests (not graded)
    {
    	int [][]blocks = new int[3][3];
    	
    	blocks[0][0] = 8;
    	blocks[0][1] = 1;
    	blocks[0][2] = 3;
    	
    	blocks[1][0] = 4;
    	blocks[1][1] = 0;
    	blocks[1][2] = 2;
    	
    	blocks[2][0] = 7;
    	blocks[2][1] = 6;
    	blocks[2][2] = 5;
    	Board board = new Board(blocks);
    	
    	System.out.println(board.manhattan());
    	System.out.println(board.toString());
    	for (Board it : board.neighbors()) {
			System.out.println(it.toString());
    	}
    	
    	System.out.println(board.twin().toString());
    	
    }
}
import edu.princeton.cs.algs4.MinPQ;
import edu.princeton.cs.algs4.Stack;

public class Solver {
	private SearchNode currentNode;
	private SearchNode currentTwinNode;
	private Stack<Board> stackBoard;
	
	private class SearchNode implements Comparable<SearchNode>
	{
		private final Board board;   // 当前棋盘情况
		private final int moves;    // 当前棋盘移动步数
		private SearchNode preSearcchNode = null;  // 当前棋盘前身
		private final int priority;   // 当前状态优先级
		
		public SearchNode(Board board, SearchNode pNode) 
		{
			this.board = board;
			this.preSearcchNode = pNode;
			if (preSearcchNode == null) 
				moves = 0;
			else
				moves = preSearcchNode.getMoves() + 1;
			priority = board.manhattan() + getMoves();
		}
	
		public int compareTo(SearchNode otherNode) 
		{
			return Integer.compare(this.getPriority(), otherNode.getPriority());
		}
		
		public int getPriority() {
			return priority;
		}
		public Board getBoard()
		{
			return board;
		}
		public int getMoves()
		{
			return moves;
		}
		public SearchNode getPreNode() 
		{
			return preSearcchNode;
		}
		
	}
	
    public Solver(Board initial)           // 找到初始板的解决方案(使用A*算法)
    {
    	if (initial == null) 
    		throw new java.lang.IllegalArgumentException("The intial Board is null");
    	
    	currentNode = new SearchNode(initial, null);  // 初始
    	MinPQ<SearchNode> minInitialPQ = new MinPQ<>();
    	minInitialPQ.insert(currentNode);
    	
    	currentTwinNode = new SearchNode(initial.twin(), null); // 交换两个位置后的
    	MinPQ<SearchNode> minTwinNode = new MinPQ<>();
    	minTwinNode.insert(currentTwinNode);
    	
    	boolean flag = false;
    	while (flag != true) 
    	{
    		// 对原始棋盘进行操作
    		currentNode = minInitialPQ.delMin();  // 取树中优先级最小的值
    		if (currentNode.getBoard().isGoal())   // 有解
    			flag = true;
    		else
    			putNeighborsBoardToPQ(currentNode, minInitialPQ); // 将邻居步骤放入树中
    		
    		// 对调整顺序的棋盘进行操作
    		currentTwinNode = minTwinNode.delMin();   // 取树中优先级最小的树
    		if (currentTwinNode.getBoard().isGoal()) 
    			flag = true;
			 else 
				 putNeighborsBoardToPQ(currentTwinNode, minTwinNode);     // 将邻居步骤放入树中
		}
    }
    
    // 将邻居情况插入到树中
    private void putNeighborsBoardToPQ(SearchNode currentNode, MinPQ<SearchNode> pMinPQ) 
	{
		Iterable<Board> boards = currentNode.getBoard().neighbors(); // 所有邻居情况
		for (Board neighborsBoard : boards) {
			if (currentNode.getPreNode() == null || 
					neighborsBoard.equals(currentNode.getPreNode().getBoard()) != true)   // 防止回退现象
				pMinPQ.insert(new SearchNode(neighborsBoard, currentNode));  // 将邻居情况插入树中
		}
	}
	
    public boolean isSolvable()            // 初始板是可解的吗?
    {
    	return currentNode.getBoard().isGoal();
    }
    public int moves()                     // 求解初始板的最小移动数;如果不可解,则为-1
    {
    	if (isSolvable()) 
    		return currentNode.getMoves();
    	else 
    		return -1;
    }
    public Iterable<Board> solution()      // 最短解中的板序列;若不可解则为空
    {
    	if (isSolvable() != true) 
			return null;
    	stackBoard = new Stack<>();
    	SearchNode nowNode = currentNode;
    
    	while (nowNode != null) {
			stackBoard.push(nowNode.getBoard());
			nowNode = nowNode.getPreNode();
		}
    	
    	return stackBoard;
    		
    }
    public static void main(String[] args) // solve a slider puzzle (given below)
    {
    	int [][]blocks = new int[3][3];
    	
    	blocks[0][0] = 8;
    	blocks[0][1] = 1;
    	blocks[0][2] = 3;
    	
    	blocks[1][0] = 4;
    	blocks[1][1] = 0;
    	blocks[1][2] = 2;
    	
    	blocks[2][0] = 7;
    	blocks[2][1] = 6;
    	blocks[2][2] = 5;
    	Board board = new Board(blocks);
    	
    	Solver solver = new Solver(board);
    	System.out.println(solver.currentNode.getPreNode() == null);
    	System.out.println(solver.currentNode.getPreNode());
    	if (solver.isSolvable() != false) {
			System.out.println("this board is can't resolve");
		}
    	
    	Iterable<Board> bIterable = solver.solution();
    	System.out.println(bIterable.toString());
    	System.out.println("444");
    	
    	for (Board it : bIterable) {
    		System.out.println(it.toString());
    	}
   
    }
}

 

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值