穷举法是所有最优化问题和多解问题的通用解法。在用穷举法求解的过程中,很多候选的解可以在求解中途被约束条件淘汰点,从而降低求解的复杂度。基于这种思想引出了回溯法。
回溯法是穷举法的一个改进,因为它也是一种通用的算法。一个问题可能会有多种可能解,这些可能解构成的问题的可能解空间, 可能解空间不是解的集合而是可能解的集合,解空间是可能解的一个子集。问题的解会从解空间中出。回溯法的基本思想是将问题的可能解空间转换成一颗树,使得这棵树的一个路径就是这个问题的一个可能解,这个树的所有路径就是这个问题的可能解空间。之后运用深度优先算法去遍历解空间树,假设遍历到某个节点时这个节点已经不符合约束条件或者不能满足最优条件,则不再搜索这个节点的所有子节点了,从而降低的计算的复杂度。但是前提是这个问题的解要能满足:假设(x0, x1, x2)是问题的可能解空间,当x0=某个确定值时,已经不满足条件,那么接下来,不管x1和x1取任何值也不能满足条件, 从而不用再从x0在往下所搜索。
例子两个经典问题;
1、0/1背包问题,假设有n个物品,重量和价值都确定,背包的重量也确定,求在背包不超重的前提下,最多能带多少价值的物品?
2、皇后问题,假设n*n的一个棋盘放置n个皇后,n个皇后都不能在同一个行,同一列,同一个对角线,问总共有多少种摆法?
用回溯法求解:
0/1背包问题的可能解空间是(x0, x1,x2),假设n = 3;其中x0,x1,x2取值都为0或1,表示物品0,物品1,物品2,是否有在背包中,1表示在,0表示不在。将可能解空间换成树,树的一个路径就是一个可能解,如图所示为树的结构,之后求解。经满足条件的解保存,并排序选出最优解。
2、皇后问题假设n = 4;则可能解空间为(x0, x1, x2, x3)其中xi取值为0到3,xi表示第i行皇后放置的位置,将转换为树,即可用广度优先遍历的思想去求解。
代码实现:
控制台输出:
背包所盛放物品的最大价值为:
60
所盛放物品编号为
[1, 2]
代码:
package AlgorithmTest;
import java.util.ArrayList;
import java.util.Collections;
/**
* Created by dell on 2016/12
*用回溯法求解01背包问题,回溯法可以求解最优解问题和求出多个符合要求的解。关键是要讲问题的解空间转换成数,使得树的一条路径就是一个解。.
*/
public class BagProblem {
public static void main(String[] args) {
int[] weights = new int[]{ 18, 14, 16}; //三个物品的重量
int[] vaules = new int[]{ 48, 30, 30}; //三个物品的价值
int bagHodler = 30; //bag的承重为30,想要知道将将那些物品放入bag中能使价值最大,且不超重
SolveSpaceTree solveSpaceTree = new SolveSpaceTree(weights, vaules, bagHodler);
Result result = solveSpaceTree.getSolve();
System. out.println( "背包所盛放物品的最大价值为:");
System. out.println(result. value);
System. out.println( "所盛放物品编号为");
System. out.println(result. ojectNos.toString());
}
//解空间树
private static class SolveSpaceTree{
private int bagHolder;
private TreeNode[] nodes;
public SolveSpaceTree( int[] weights, int[] values, int bagHodler){
this. bagHolder = bagHodler;
nodes = new TreeNode[( int)Math. pow( 2, weights. length + 1) - 1];
nodes[ 0] = new TreeNode(- 1, false, 0, 0); //第一个节点是空几点不代表哪个物品
int index = 1;
for ( int i = 0; i < weights. length; i++){
for ( int j = 0; j <= i; j++){
nodes[index++] = new TreeNode(i, false, weights[i], values[i]);
nodes[index++] = new TreeNode(i, true, weights[i], values[i]);
}
}
}
public Result getSolve(){
ArrayList<Result> results = new ArrayList<Result>();
getSolve( 0, new Result(), results);
Collections. sort(results);
return results.get(results.size() - 1);
}
public void getSolve( int node, Result result, ArrayList<Result> results){
if (node >= nodes. length){
results.add(result);
return ;
}
int currentWeight = nodes[node]. occur? nodes[node]. weight: 0;
int currentValue = nodes[node]. occur? nodes[node]. value: 0;
if (currentWeight + result. weightSum <= this. bagHolder){ //符合约束条件,往下走
if ( nodes[node]. occur){
Result result1 = new Result();
result1. weightSum = currentWeight + result. weightSum;
result1. value = currentValue + result. value;
result1. ojectNos.addAll(result. ojectNos);
result1. ojectNos.add( nodes[node]. objectNo);
getSolve( 2 * node + 1 ,result1, results);
getSolve( 2 * node + 2 ,result1, results);
} else{
getSolve( 2 * node + 1 ,result, results);
getSolve( 2 * node + 2 ,result, results);
}
} else{
if (result. weightSum <= this. bagHolder){
results.add(result);
}
}
}
}
public static class Result implements Comparable<Result>{
private int weightSum = 0;
private int value = 0;
private ArrayList<Integer> ojectNos = new ArrayList<Integer>();
@Override
public int compareTo(Result o){
return this. value - o. value;
}
}
public static class TreeNode{
private boolean occur; //出现与否
private int weight; //重
private int value; //价值
private int objectNo; //节点代码的物品
public TreeNode( int objectNo, boolean occur, int weight, int value) {
this. occur = occur;
this. weight = we
import java.util.ArrayList;
import java.util.Collections;
/**
* Created by dell on 2016/12
*用回溯法求解01背包问题,回溯法可以求解最优解问题和求出多个符合要求的解。关键是要讲问题的解空间转换成数,使得树的一条路径就是一个解。.
*/
public class BagProblem {
public static void main(String[] args) {
int[] weights = new int[]{ 18, 14, 16}; //三个物品的重量
int[] vaules = new int[]{ 48, 30, 30}; //三个物品的价值
int bagHodler = 30; //bag的承重为30,想要知道将将那些物品放入bag中能使价值最大,且不超重
SolveSpaceTree solveSpaceTree = new SolveSpaceTree(weights, vaules, bagHodler);
Result result = solveSpaceTree.getSolve();
System. out.println( "背包所盛放物品的最大价值为:");
System. out.println(result. value);
System. out.println( "所盛放物品编号为");
System. out.println(result. ojectNos.toString());
}
//解空间树
private static class SolveSpaceTree{
private int bagHolder;
private TreeNode[] nodes;
public SolveSpaceTree( int[] weights, int[] values, int bagHodler){
this. bagHolder = bagHodler;
nodes = new TreeNode[( int)Math. pow( 2, weights. length + 1) - 1];
nodes[ 0] = new TreeNode(- 1, false, 0, 0); //第一个节点是空几点不代表哪个物品
int index = 1;
for ( int i = 0; i < weights. length; i++){
for ( int j = 0; j <= i; j++){
nodes[index++] = new TreeNode(i, false, weights[i], values[i]);
nodes[index++] = new TreeNode(i, true, weights[i], values[i]);
}
}
}
public Result getSolve(){
ArrayList<Result> results = new ArrayList<Result>();
getSolve( 0, new Result(), results);
Collections. sort(results);
return results.get(results.size() - 1);
}
public void getSolve( int node, Result result, ArrayList<Result> results){
if (node >= nodes. length){
results.add(result);
return ;
}
int currentWeight = nodes[node]. occur? nodes[node]. weight: 0;
int currentValue = nodes[node]. occur? nodes[node]. value: 0;
if (currentWeight + result. weightSum <= this. bagHolder){ //符合约束条件,往下走
if ( nodes[node]. occur){
Result result1 = new Result();
result1. weightSum = currentWeight + result. weightSum;
result1. value = currentValue + result. value;
result1. ojectNos.addAll(result. ojectNos);
result1. ojectNos.add( nodes[node]. objectNo);
getSolve( 2 * node + 1 ,result1, results);
getSolve( 2 * node + 2 ,result1, results);
} else{
getSolve( 2 * node + 1 ,result, results);
getSolve( 2 * node + 2 ,result, results);
}
} else{
if (result. weightSum <= this. bagHolder){
results.add(result);
}
}
}
}
public static class Result implements Comparable<Result>{
private int weightSum = 0;
private int value = 0;
private ArrayList<Integer> ojectNos = new ArrayList<Integer>();
@Override
public int compareTo(Result o){
return this. value - o. value;
}
}
public static class TreeNode{
private boolean occur; //出现与否
private int weight; //重
private int value; //价值
private int objectNo; //节点代码的物品
public TreeNode( int objectNo, boolean occur, int weight, int value) {
this. occur = occur;
this. weight = we