Week 4 編程作業:8 Puzzle

編程作業: 8 Puzzle

只有99分

Board.java

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

public class Board {

	private final short[] blocks;
	private final int n;
	private int i0;
	private int hamming;
	private int manhattan;
	private long hash;

	public Board(int[][] blocks) {
		this(blocks.length, copy(blocks));
	}

	private Board(int n, short[] blocks) {
		this.blocks = Arrays.copyOf(blocks, blocks.length);
		this.n = n;
		this.hash = 0;

		for (int i = 0; i < blocks.length; i++) {
			if (this.blocks[i] == 0) {
				this.i0 = i;
			} else if (this.blocks[i] != i + 1) {
				this.hash *= 11;
				this.hash += this.blocks[i];
				hamming++;
				int i1 = this.blocks[i] - 1;
				manhattan += Math.abs((i / n) - (i1 / n)) + Math.abs((i % n) - (i1 % n));
			}
		}
	}

	private Board(int n, short[] blocks, int manhattan) {
		this.blocks = Arrays.copyOf(blocks, blocks.length);
		this.n = n;
		this.hash = 0;
		this.manhattan = manhattan;

		for (int i = 0; i < blocks.length; i++) {
			if (this.blocks[i] == 0) {
				this.i0 = i;
			} else if (this.blocks[i] != i + 1) {
				this.hash *= 11;
				this.hash += this.blocks[i];
				hamming++;
			}
		}
	}

	private static short[] copy(int[][] ar) {
		short[] rtn = new short[ar.length * ar.length];
		for (int r = 0; r < ar.length; r++) {
			for (int c = 0; c < ar.length; c++) {
				rtn[rcToIndex(ar.length, r, c)] = (short) ar[r][c];
			}
		}
		return rtn;
	}

	/** r=0~n-1, c=0~n-1, index=0~n^2-1 */
	private static int rcToIndex(int n, int r, int c) {
		return n * r + c;
	}

	/** r=0~n-1, c=0~n-1, i=1~n^2 */
	private static int[] indexToRc(int n, int i) {
		i--;
		return new int[] { i / n, i % n };
	}

	public int dimension() {
		return this.n;
	}

	public int hamming() {
		return hamming;
	}

	public int manhattan() {
		return manhattan;
	}

	public boolean isGoal() {
		return hamming == 0;
	}

	public Board twin() {
		int i1 = i0 >= n ? i0 - n : i0 + n;
		int i2 = i1 % n > 0 ? i1 - 1 : i1 + 1;

		swap(this.blocks, i1, i2);
		Board rtn = new Board(this.n, this.blocks);
		swap(this.blocks, i1, i2);
		return rtn;
	}

	private static void swap(short[] ar, int i, int j) {
		short temp = ar[i];
		ar[i] = ar[j];
		ar[j] = temp;
	}

	public boolean equals(Object y) {
		if (!(y instanceof Board))
			return false;

		Board by = (Board) y;
		if (this.hash != by.hash)
			return false;

		for (int i = 0; i < this.blocks.length; i++) {
			if (this.blocks[i] != by.blocks[i])
				return false;
		}
		return true;
	}

	public Iterable<Board> neighbors() {
		return createNeighbors();
	}

	private List<Board> createNeighbors() {
		List<Board> list = new ArrayList<>(4);

		final int curC = i0 % n;
		final int curR = i0 / n;

		if (i0 >= n) {
			// 空白上移、數字下移
			swap(this.blocks, i0, i0 - n);
//			list.add(new Board(this.n, this.blocks));
			int realR = (this.blocks[i0] - 1) / n;
			list.add(new Board(this.n, this.blocks, this.manhattan + (realR >= curR ? -1 : 1)));
			swap(this.blocks, i0, i0 - n);
		}
		if (i0 < this.blocks.length - n) {
			// 空白下移、數字上移
			swap(this.blocks, i0, i0 + n);
//			list.add(new Board(this.n, this.blocks));
			int realR = (this.blocks[i0] - 1) / n;
			list.add(new Board(this.n, this.blocks, this.manhattan + (realR <= curR ? -1 : 1)));
			swap(this.blocks, i0, i0 + n);
		}
		if (i0 % n > 0) {
			// 空白左移、數字右移
			swap(this.blocks, i0, i0 - 1);
//			list.add(new Board(this.n, this.blocks));
			int realC = (this.blocks[i0] - 1) % n;
			list.add(new Board(this.n, this.blocks, this.manhattan + (realC >= curC ? -1 : 1)));
			swap(this.blocks, i0, i0 - 1);
		}
		if (i0 % n < n - 1) {
			// 空白右移、數字左移
			swap(this.blocks, i0, i0 + 1);
//			list.add(new Board(this.n, this.blocks));
			int realC = (this.blocks[i0] - 1) % n;
			list.add(new Board(this.n, this.blocks, this.manhattan + (realC <= curC ? -1 : 1)));
			swap(this.blocks, i0, i0 + 1);
		}
		return list;
	}

	public String toString() {
		StringBuilder s = new StringBuilder();
		s.append(n + "\n");
		for (int i = 0; i < this.blocks.length; i++) {
			s.append(String.format("%2d ", blocks[i]));
			if (i % n == n - 1)
				s.append("\n");
		}
		return s.toString();
	}

}

Solver.java

import java.util.LinkedList;

import edu.princeton.cs.algs4.MinPQ;

public class Solver {

	private final Node goal;

	public Solver(Board initial) {
		if (initial == null)
			throw new java.lang.IllegalArgumentException();

		this.goal = aStar(initial);
	}

	private static Node aStar(Board initBoard) {
		MinPQ<Node> pq = new MinPQ<>(16);

		pq.insert(new Node(null, initBoard, 0, true));
		pq.insert(new Node(null, initBoard.twin(), 0, false));

		while (!pq.isEmpty()) {
			Node cur = pq.delMin();
			if (cur.board.isGoal())
				return cur;

			for (Board neighbor : cur.board.neighbors()) {
				if (cur.pre == null || !neighbor.equals(cur.pre.board))
					pq.insert(new Node(cur, neighbor, cur.move + 1, cur.isInitBoard));
			}
		}
		return null;
	}

	private static class Node implements Comparable<Node> {
		final Node pre;
		final Board board;
		final int move;
		final int priority;
		final boolean isInitBoard;

		Node(Node pre, Board board, int move, boolean isInitBoard) {
			this.pre = pre;
			this.board = board;
			this.move = move;
			this.isInitBoard = isInitBoard;
			// 增加manhattan比重更快找到解,但不是最短路徑
//			this.priority = (board.manhattan() * 5 + move) * 2 + board.hamming();
			this.priority = board.manhattan() + move;
		}

		@Override
		public int compareTo(Node o) {
			// 這裡用if都會超時
//			if( this.priorityM == o.priorityM )
//				return this.priorityH - o.priorityH;
//			return this.priorityM - o.priorityM;
			return this.priority - o.priority;
		}

	}

	private static int priorityH(Node n) {
		return n.board.hamming() + n.move;
	}

	private static int priorityM(Node n) {
		return n.board.manhattan() + n.move;
	}

	public boolean isSolvable() {
		return this.goal != null && this.goal.isInitBoard;
	}

	public int moves() { // min number of moves to solve initial board; -1 if unsolvable
		if (isSolvable())
			return this.goal.move;
		return -1;
	}

	public Iterable<Board> solution() { // sequence of boards in a shortest solution; null if unsolvable
		if (!isSolvable())
			return null;

		LinkedList<Board> rtn = new LinkedList<>();
		Node cur = this.goal;
		while (cur != null) {
			rtn.addFirst(cur.board);
			cur = cur.pre;
		}
		return rtn;
	}

}