状态空间图

用于呈现问题求解的过程。其中问题被划分为多个状态,图中每个节点代表一种可能的状态,节点之间的连线则代表状态转换的操作,从起始状态到目标状态的路径,就是问题的解

以假币问题为例,6枚硬币,其中存在1枚假币。如何用秤测量3次,识别出假币?

下面的状态空间图展示了问题求解的过程。

其中,节点代表问题的状态。以带*的节点为例,[C1 C2 C3 C4] 代表在这4枚硬币中存在假币。C1C2 : C5C6,表示将C1C2放左侧,C5C6放右侧,称量两侧重量是否相等。

节点之间的连线,代表了导致状态转换的操作,这里是称量结果相等,或不相等
Java回溯法解n皇后问题

回溯法

回溯法将问题求解划分为多个状态,状态之间的转换需要符合问题的约束条件。回溯法首先尝试不断深入探索状态空间图,当第n步没有符合约束的合法转换时,回溯法退回第n-1步,并撤销第n-1步的操作,重新选择其他合法的状态,若第n-1步仍然没有其他合法状态转换,则继续退回第n-2步,以此类推。

回溯法求解n皇后问题

在n皇后问题中,以回溯法求解步骤为:

确定约束条件:
    棋盘上任意两个子,不能在同一行,同一列,同一对角线

求解步骤:
1.第n步,确定第n行的落子,即每一步从1~n列选择1个合法的列m,其中m的取值必须符合约束条件,并且约定,优先选择序号小的列。
2.当第n步,没有合法的列可选,则退回n-1步,撤销n-1步的选择,在第n-1步中尝试下一个合法的列。

Java代码:

package cn.lin.test;

import java.util.Arrays;

/**
 * 	八皇后问题
 * 	回溯:
 * 		当状态不合法,回溯到上一步,重新选择
 * 	约束条件:
 * 		不能在同一行、同一列、对角线
 * @author ljf
 *
 */
public class NQuenesSolver {
	
	// n皇后
	private final int n;
	
	/**
	 * 	路径:数组下标对于行,数组元素值对应列
	 */
	private int[] path;
	
	// 当前探索到第几行
	private int latestRow;
	
	public NQuenesSolver(int n) {
		this.n = n;
		path = new int[n];
		//初始-1,表示每一行都没放皇后
		Arrays.fill(path, -1);
		latestRow = 0;
	}
	
	/**
	 * 
	 * @return 每1行中,棋子所在列
	 */
	public int[] search() {
		while (true) {
			//无条件向前走
			moveForward();
			//状态合法吗?
			if (!checkState()) {
				//还可以向前走吗?
				if (hasChoice()) {
					continue;
				} else {
					//回溯
					reset();
					continue;
				}
			}
			System.out.println("lastestRow:" + latestRow + ", path:" + Arrays.toString(path));
			if (isSolved()) {
				return path;
			}
			//下一行
			latestRow++;
		}
	}
	
	boolean hasChoice() {
		return path[latestRow] < n - 1;
	}
	
	private void moveForward() {
		path[latestRow]++;
	}
	
	//先会判断状态是否合法,如果有n个合法棋子,说明问题已解
	private boolean isSolved() {
		if (latestRow == n - 1) {
			return true;
		}
		return false;
	}
	
	/**
	 * 	回溯
	 */
	private void reset() {
		while (true) {
			path[latestRow] = -1;
			latestRow--;
			if (hasChoice()) {
				break;
			}
		}
	}
	
	private boolean checkState() {
		if (latestRow == 0) {
			return true;
		}
		//只确定最后一个皇后是否合法
		for (int i = 0; i < latestRow; i++) {
			if (!isLegal(i, path[i], latestRow, path[latestRow])) {
				return false;
			}
		}
		return true;
	}
	
	private boolean isLegal(int x1, int y1, int x2, int y2) {
		//不在同一行或同一列
		if (x1 == x2 || y1 == y2) {
			return false;
		}
		//计算斜率,对角线上,则斜率为1或-1
		double slash = (y2 - y1) * 1.0 / (x2 - x1);
		//不在对角线
		if (slash == -1 || slash == 1) {
			return false;
		}
		return true;
	}

	public static void main(String[] args) {
		NQuenesSolver eightQuenes = new NQuenesSolver(8);
		System.out.println(Arrays.toString(eightQuenes.search()));
	}
}

输出:
Java回溯法解n皇后问题

相关文章: