文章目录
一、理论基础
1.1 基本策略
1.2 使用步骤
1.3 经典例子
二、常见例子
2.1 八皇后问题
2.2 装载问题
2.3 批量作业调度问题
2.4 背包问题
一、理论基础
  回溯法作为一种常见的算法思想,其概念为:按选优条件向前搜索,以达到目标。当探索到某一步时,发现原先选择并不优或达不到目标,就退回一步重新选择,这种走不通就退回再走的技术为回溯法,而满足回溯条件的某个状态的点称为“回溯点”。回溯法和穷举法的思想相近,不同在于穷举法是将所有的情况都列举出来以后再进行筛选,而回溯法在列举过程如果发现当前情况不满足要求,就返回上一步进行新的尝试。

1.1 基本策略
  回溯法的策略是:在包含问题的所有解的解空间树中,按照深度优先搜索的策略,从根结点出发深度探索解空间树。当探索到某一结点时,要先判断该结点是否包含问题的解,如果包含,就从该结点出发继续探索下去,如果该结点不包含问题的解,则逐层向其祖先结点回溯,直到探索到叶节点则结束本次遍历。

1.2 使用步骤
  使用回溯法的基本步骤:
   1>定义问题的解空间。
   2>确定易于搜索的解空间结构。
   3>以深度优先搜索的策略搜索解空间,并在搜索过程中用剪枝函数避免无效搜索。
  以上是比较官方说法,翻译成较容易理解的步骤是:

backtrack(开始回溯的初始条件){
   if(结束回溯条件){
    保存本趟完整的回溯结果
    return;
  }
  if(满足本次判断条件){
    记录本趟某一次的回溯结果
    backtrack(进行下一模块的回溯);
    回退本趟某一次的回溯结果
  }
}

1.3 经典例子
  常见例子如下:
   1>八皇后问题
   2>装载问题
   3>批处理作业调度问题
   4>背包问题
  接下来将对这些例子进行实现,来探究回溯法的具体使用方法。

二、常见例子
2.1 八皇后问题
  该问题的描述是:在8×8格的国际象棋上摆放8个皇后,使其不能互相攻击,即任意两个皇后都不能处于同一行、同一列或同一斜线上,问有多少种摆法。该问题有多种解法,递归法是最简单的一种,本文使用递归法,来借这个问题来介绍回溯法。
  如果是初次接触该问题,一看到这个题目可能会觉得手足无措。可以尝试着将题目转换为:在一个8x8的二维数组上,每次在每一行放置一个元素,使得这8行的元素互相不在同一行、同一列或同一斜线上。假如8x8棋盘原始的元素都是0,在某行某列放置皇后后,该位置的元素改为1。所以可以通过该列的8行是否存在元素1,来判断该列是否存在皇后。将八皇后问题转换成这样的描述后,该题目就转换成了解决两个问题:

判断要放置的元素和之前放置过的元素互相不在同一行、同一列或同一斜线上?
  在解决该问题上,判断同一列上是否存在皇后时,就遍历一行中的每一列,如果有,就不能再放置皇后元素。示例代码如下:

/*判断同一列中是否存在1,即皇后*/
		for(i=0;i<8;i++){
			if(chess[i][col]==1)
				return false;
		}
如何撤销该行中放置的皇后?
  在撤销的操作上,可以将改行中放置的皇后位置上的元素置为0,然后重新遍历该行。比如在 i 行 j 列放置了皇后,但是在i+1行放置元素时,通过判断得知,i+1 行的八个位置都不能放置皇后,此时就只能将 i 行 j 列的皇后撤销,继续尝试 i 行 j 列后面的元素。示例代码如下:
			if(isExistQueen(row,col)){
				/*该位置放置皇后*/
				chess[row][col]=1;
				/*然后继续在下一行进行判断*/
				eightQueen(row+1);
				/*清零,这也是回溯法要注意的地方,一种方法尝试后,需要将之前做的尝试回退,以免影响到下一次尝试*/
				chess[row][col]=0;           
			}



  基于上面的知识铺垫,我们就可以推导出用递归方法解决八皇后问题的步骤:
   1>搜索。从下标为0的行开始,尝试在该行的某个列放置皇后,然后继续进行下一行的搜索,也就是进行递归的过程。
   2>输出,当搜索的行下标为8时,代表该次搜索已经完成,可以输出结果,总的可能性+1。
   3>判断。这是八皇后算法的核心,判断某行某列的位置上是否可以放置皇后。
  示例代码如下:

package Recall;
/*
 * 在8×8格的国际象棋上摆放8个皇后,使其不能互相攻击,即任意两个皇后都不能处于同一行、同一列或同一斜线上,问有多少种摆法。
 */
public class EightQueen {

	/*总的可能数,初始化为0,每输出一次结果,自增1*/
	private static int count=0;
	/*创建一个8x8的棋盘,默认初始化元素为0,代表未放置皇后*/
	private static int[][] chess=new int[8][8];

	public static void main(String[] args) {
		/*传参0,代表从第一行开始遍历,寻找放置皇后的位置*/
		eightQueen(0);
		System.out.println("八皇后问题总共有"+count+"种结果");
	}

	private static void eightQueen(int row){
		/*如果遍历完八行都找到放置皇后的位置则打印*/
		if(row>7){                       
			printQueen();                       
			count++;
			return;
		}
		/*在每一行放置皇后,即遍历某行中的每一列*/
		for(int col=0;col<8;col++){
			/*判断是否可以放置皇后*/
			if(isExistQueen(row,col)){
				/*该位置放置皇后*/
				chess[row][col]=1;
				/*然后继续在下一行进行判断*/
				eightQueen(row+1);
				/*清零,这也是回溯法要注意的地方,一种方法尝试后,需要将之前做的尝试回退,以免影响到下一次尝试*/
				chess[row][col]=0;           
			}
		}
	}

	private static void printQueen(){
		System.out.println("第 "+(count+1)+"种结果:");
		for(int row=0;row<8;row++){
			for(int col=0;col<8;col++){
				/*放置皇后*/
				if(chess[row][col]==1){
					System.out.print("q ");
				/*放置士兵*/
				}else{
					System.out.print("s ");
				}
			}
			System.out.println();
		}
		System.out.println();
	}

	private static boolean isExistQueen(int row,int col){
		int i,k;
		/*判断同一列中是否存在1,即皇后*/
		for(i=0;i<8;i++){
			if(chess[i][col]==1)
				return false;
		}
		/*判断从(0,0)到(i,k)区域内,即左上角的对角线位置上是否存在1,即皇后
		*此时不检查左下角,是因为左下角对应的行还没开始放置皇后,不用检查*/ 
		for(i=row,k=col;i>=0&&k>=0;i--,k--){
			if(chess[i][k]==1)
				return false;
		}
		/*判断从(0,7)到(i,k)区域内,即右上角的对角线位置上是否存在1,即皇后
		*此时不检查右下角,是因为右下角对应的行还没开始放置皇后,不用检查*/ 
		for(i=row,k=col;i>=0&&k<8;i--,k++){
			if(chess[i][k]==1)
				return false;
		}
		return true;
	}

}

  部分测试结果如下:

第 92种结果:
s s s s s s s q
s s s q s s s s
q s s s s s s s
s s q s s s s s
s s s s s q s s
s q s s s s s s
s s s s s s q s
s s s s q s s s
八皇后问题总共有92种结果

2.2 装载问题
  该问题的描述是:一批集装箱共n个,要装上2艘载重量分别为c1和c2的轮船,其中集装箱i的重量为Wi且W1+W2+……+Wn<=c1+c2;试确定一个合理的装载方案使这n个集装箱装上这两艘轮船。
  解决该问题的思路是:先确定是否有解?当W1+W2+……+Wn<=c1+c2时,就代表该问题有解。然后在有解的情况下,进行拆解该问题,可分为两步:
   1>首先将第一艘轮船尽可能装满(在这步中体现了回溯的思想)。
   2>然后将剩余的集装箱装在第二艘轮船上。
  与八皇后问题的解题思路是一样的,不同之处在于解题过程中有这更多的变量。先展示一下解该问题需要的变量:

/*货箱重量数组*/
	static int[] weights={20,30,60,40,40};
	/*货箱数目*/
	static int boxNum=weights.length;
	/*第一艘船的最大承载量*/
	static int oneShipCapcity=100;
	/*第二艘船的最大承载量*/
	static int twoShipCapcity=100;
	/*当前装载的重量*/
	static int currentWeight=0;
	/*目前最优装载的重量*/
	static int bestWeight=0;
	/*当前解,记录每箱是否装得下的数组,用1和0表示*/
	static int[] currentAnswer=new int[boxNum];;
	/*最优解,记录每箱是否装得下的数组,用1和0表示*/
	static int[] bestAnswer=new int[boxNum];
	/*剩余货箱的重量,即总的重量-装上第一艘船的重量*/
	static int leftWeight;



  接下来,一个一个看一下这些变量。weights数组用来存储每个货箱的重量,也就是需要装到两艘船的元素;boxNum代表货箱数量;oneShipCapcity代表第一艘船的最大载重量,用来衡量哪些货箱可以装到第一艘船;twoShipCapcity代表第一艘船的最大载重量,用来衡量剩下的货箱能否完全装到第二艘船上;currentWeight代表该次尝试装载过程中装载到第一艘船上的总重量,也就是解集树上的一个子树;bestWeight代表第一艘船上的最优装载量,其实就是满足currentWeight<=oneShipCapcity条件时的currentWeight;currentAnswer代表当次子集,用0和1表示是否能将某货箱装到第一艘船上;bestAnswer同理,是满足currentWeight<=oneShipCapcity条件时currentAnswer;leftWeight是将某些货箱装到第一艘船上后剩余重量,leftWeight初始值为所有货箱的总重量,如下:

//初始化leftWeight,即剩余最大重量
		for(int i=0;i<boxNum;i++) {
			leftWeight+=weights[i];
		}



  基于上面的知识铺垫,我们就可以推导出用递归方法解决装载问题的步骤:
   1>使用回溯法装第一艘船。从下标为0的货箱数组开始装载,在第一艘船上的最重量<=第一艘船总装载量的情况下,继续装载,也就是进行递归的过程。
   2>当装到最后一箱货物时,代表已经装载完第一艘船,接下来计算在第二艘上能否装载完剩余货箱。
   3>在第二艘上能否装载完剩余货箱的情况下,装载完货物,输出具体装载情况。
  示例代码如下:

package Recall;
/*
 * 1.具体问题
 *	  一批集装箱共n个要装上2艘载重量分别为c1和c2的轮船,其中集装箱i的重量为Wi且
 *	 W1+W2+……+Wn<=c1+c2;试确定一个合理的装载方案使这n个集装箱装上这两艘轮船。
 * 2.问题分析
 *   如果一个装载问题有解,则采用下面的策略可以得到最优装载方案:
 *     1)首先将第一艘轮船尽可能装满;
 *     2)然后将剩余的集装箱装在第二艘轮船上。
 */
public class MaxLoading {

	/*货箱重量数组*/
	static int[] weights={20,30,60,40,40};
	/*货箱数目*/
	static int boxNum=weights.length;
	/*第一艘船的最大承载量*/
	static int oneShipCapcity=100;
	/*第二艘船的最大承载量*/
	static int twoShipCapcity=100;
	/*当前装载的重量*/
	static int currentWeight=0;
	/*目前最优装载的重量*/
	static int bestWeight=0;
	/*当前解,记录每箱是否装得下的数组,用1和0表示*/
	static int[] currentAnswer=new int[boxNum];;
	/*最优解,记录每箱是否装得下的数组,用1和0表示*/
	static int[] bestAnswer=new int[boxNum];
	/*剩余货箱的重量,即总的重量-装上第一艘船的重量*/
	static int leftWeight;

	public static void main(String[] args) {
		//初始化leftWeight,即剩余最大重量
		for(int i=0;i<boxNum;i++) {
			leftWeight+=weights[i];
		}
		//计算最优载重量
		backtrack(0);

		/*第二艘船可能要装的重量*/
		int weight2 = 0;
		for(int i=0;i<weights.length;i++){
			/*1-bestAnswer[i],可以将bestAnswer数组中的值进行0-1
			 * 反转,即将不装上第一艘船的箱子全都装上第二艘船
			 */
			weight2 += weights[i]*(1-bestAnswer[i]);
		}
		if(weight2>twoShipCapcity){
			System.out.println("无法载满货物");
		}else{
			System.out.println("第一艘船装载货物的重量: " + bestWeight);
			System.out.println("第二艘船装载货物的重量: " + weight2);
			for(int i=0;i<weights.length;i++){
				//第一艘船的装载情况
				if(bestAnswer[i]==1){
					System.out.println("第"+(i+1)+"件货物装入第一艘船");
				//第二艘船的装载情况
				}else{
					System.out.println("第"+(i+1)+"件货物装入第二艘船");
				}
			}
		}
	}

	/*利用回溯思想尽量将第一艘船装满*/
	public static void backtrack(int num){
		/*已经尝试了装载最后一个元素*/
		if(num==boxNum){
			/*最后时刻的装载量,可以更新为最优装载量*/
			if(currentWeight>bestWeight){
				for(int i=0;i<boxNum;i++){
					bestAnswer[i] = currentAnswer[i];
				}
				bestWeight = currentWeight;
			}
			return;
		}

		/*如果没尝试装载完最后一箱货物,继续装第num+1箱*/
		leftWeight -= weights[num];
		/*第一艘船能装下第t+1箱货物*/
		if(currentWeight + weights[num] <= oneShipCapcity){
			/*currentAnswer数组用来标识某一箱货物是否装的下,1代表装的下,0代表装不下*/
			currentAnswer[num] = 1;
			currentWeight += weights[num];
			backtrack(num+1);
			/*回溯*/
			currentWeight -= weights[num];
		}

		/*第一艘船装不下第num+1箱货物*/
		if(currentWeight + leftWeight>bestWeight){
			/*不装第num+1箱,继续装下一箱*/
			currentAnswer[num] = 0;
			backtrack(num+1);
		}
		/*因为装不下第num+1箱,所以在leftWeight中恢复该数值*/
		leftWeight += weights[num];
	}
}



  测试结果如下:

第一艘船装载货物的重量: 100
第二艘船装载货物的重量: 90
第1件货物装入第一艘船
第2件货物装入第二艘船
第3件货物装入第二艘船
第4件货物装入第一艘船
第5件货物装入第一艘船

2.3 批量作业调度问题
  该问题的描述是:给定n个作业的集合J=(J1,J2,… ,Jn)。每一个作业Ji都有两项任务分别在2台机器上完成。每个作业必须先由机器1处理,然后再由机器2处理。作业Ji需要机器j的处理时间为tji。对于一个确定的作业调度,设Fji是作业i在机器j上完成处理时间。则所有作业在机器2上完成处理时间和f=F2i,称为该作业调度的完成时间和。对于给定的n个作业,指定最佳作业调度方案,使其完成时间和达到最小。
  从题目描述可以看出,该问题和装载问题是有些类似的,不过该问题中,计算时间的过程需要简单说下:
  假设有以下任务,在机器一和机器二上所花费的时间分别如下:

任务 在机器一上所花时间 在机器二上所花时间
任务一 2 1
任务二 3 1
任务三 2 3
  假设调度方案为(1,2,3),那么,所花费的时间情况如下:
   1>作业1在机器1上完成的时间是2,在机器2上完成的时间是3
   2>作业2在机器1上完成的时间是5,在机器2上完成的时间是6
   3>作业3在机器1上完成的时间是7,在机器2上完成的时间是10
  所以,作业调度的完成时间和= 3 + 6 + 10。
  解决该问题的思路是:遍历出所有调度方案的总时间,然后选最小时间的调度方案即可。解该问题需要的变量:

/*默认作业在第一台机器、第二台机器上所花费的时间*/
	static int[][] mission={{2,1},{3,1},{2,3}};
	/*作业数*/
	static int missionNum=mission.length;
	/*默认最短时间*/
	static int bestTime=100;
	/*默认调度策略*/
	static int[] currentSchedule={0,1,2};
	/*最佳调度策略*/
	static int[] bestSchedule=new int[missionNum];
	/*每个任务的结束时间,即在第一台、第二台机器上都完成任务的时间*/
	static int[] currentOneAndTwoTime=new int[missionNum];
	/*某个任务在第一台机器上花费的时间*/
	static int currentOneTime=0;
	/*总时间*/
	static int totaltime;
1



  这些变量中,mission是一个二维数组,用来存储每个任务在机器一和机器二上作业所花费的时间;missionNum是总的作业数,等于mission的数量;bestTime是默认最短时间,因为该题是求最小时间,所以默认的时间只要给个大概的、大于所以任务调度的总时间的值即可;currentSchedule是默认调度策略,因为回溯算法,在计算的过程中,有回退操作,所以此处的默认策略并不是很重要,只要给个任务的一种排列就行;bestSchedule是最佳调度策略,记录每次比较后,花费总时间最小的调度策略;currentOneAndTwoTime是当前任务在机器一和机器二上完成所花费的时间;currentOneTime表示某任务在第一台机器上花费的时间,即之前阶段的任务时间和加上当前任务在机器一上所花费的时间;totaltime为所有任务都遍历后所花费的总时间。
  解决该问题的过程中,有两个点需要着重说明下:
   1>每种排列结束后,更新一下最优时间。示例代码如下:

/*当搜索到叶子节点后,当前调度策略就是最佳调度策略*/
		if(num>missionNum-1){
			bestTime=totaltime;
			for(int i=0;i<missionNum;i++)
				bestSchedule[i]=currentSchedule[i];
			return;



   2>在求每种任务调度的可能时,中间有剪枝操作,即任务还没排列完,用时就已经超过之前的总用时了。示例代码如下:

if(totaltime<bestTime){
				//把选择出的原来在i位置上的任务序号调到当前执行的位置num
				swap(currentSchedule,num,i);	
				BackTrack(num+1);
				//进行回溯,还原,执行该层的下一个任务。
				swap(currentSchedule,num,i);
			}
			/*如果该作业处理完之后,总时间已经超过最优时间,就直接回溯*/
			currentOneTime=currentOneTime-mission[currentSchedule[i]][0];
			totaltime=totaltime-currentOneAndTwoTime[num];

  基于上面的知识铺垫,我们就可以推导出用递归方法解决该问题的步骤:先按默认1、2、3的顺序计算总用时,然后再遍历其他调度方案的总用时,计算出最少用时。示例代码如下:package Recall;
/*
 * 给定n个作业的集合J=(J1,J2,... ,Jn)。每一个作业Ji都有两项任务分别在2台机器上完成。每个作业必须先由
 * 机器1处理,然后再由机器2处理。作业Ji需要机器j的处理时间为tji。对于一个确定的作业调度,设Fji是作业i在机器j
 * 上完成处理时间。则所有作业在机器2上完成处理时间和f=F2i,称为该作业调度的完成时间和。对于给定的n个作业,指定
 * 最佳作业调度方案,使其完成时间和达到最小。
 */

public class BatchWork {
	/*默认作业在第一台机器、第二台机器上所花费的时间*/
	static int[][] mission={{2,1},{3,1},{2,3}};
	/*作业数*/
	static int missionNum=mission.length;
	/*默认最短时间*/
	static int bestTime=100;
	/*默认调度策略*/
	static int[] currentSchedule={0,1,2};
	/*最佳调度策略*/
	static int[] bestSchedule=new int[missionNum];
	/*每个任务的结束时间,即在第一台、第二台机器上都完成任务的时间*/
	static int[] currentOneAndTwoTime=new int[missionNum];
	/*某个任务在第一台机器上花费的时间*/
	static int currentOneTime=0;
	/*总时间*/
	static int totaltime;	public static void main(String[] args){
		BackTrack(0);
		System.out.println("最佳调度方案为:");
		for(int i=0;i<missionNum;i++)
			System.out.print((bestSchedule[i]+1)+"  ");
		System.out.print("\n其完成时间为"+bestTime);
	}

	public static void BackTrack(int num){
		/*当搜索到叶子节点后,当前调度策略就是最佳调度策略*/
		if(num>missionNum-1){
			bestTime=totaltime;
			for(int i=0;i<missionNum;i++)
				bestSchedule[i]=currentSchedule[i];
			return;
		}

		for(int i=num;i<missionNum;i++){	
			/*在第一台机器上花费的时间,即二维数组的第一列值*/
			currentOneTime+=mission[currentSchedule[i]][0];
			if(num==0){
				/*第一个任务的话,直接在两台机器上花费的时间相加就行*/
				currentOneAndTwoTime[num]=currentOneTime+mission[currentSchedule[i]][1];
			}else{
				/*不是第一个任务的话,当前任务在第一台机器上所花费的时间,要选择下面两个时间的较大值:
				 * 1、前一个阶段的任务完成在机器一、机器二上所花费的总时间
				 * 2、当前阶段的任务在机器一上所花费的时间
				 */
				if(currentOneAndTwoTime[num-1]>currentOneTime){
					currentOneAndTwoTime[num]=currentOneAndTwoTime[num-1]+mission[currentSchedule[i]][1];
				}else{
					currentOneAndTwoTime[num]=currentOneTime+mission[currentSchedule[i]][1];
				}
			}
			/*总时间就等于+该任务在第一二台机器上花费的时间*/
			totaltime=totaltime+currentOneAndTwoTime[num];
			if(totaltime<bestTime){
				//把选择出的原来在i位置上的任务序号调到当前执行的位置num
				swap(currentSchedule,num,i);	
				BackTrack(num+1);
				//进行回溯,还原,执行该层的下一个任务。
				swap(currentSchedule,num,i);
			}
			/*如果该作业处理完之后,总时间已经超过最优时间,就直接回溯*/
			currentOneTime=currentOneTime-mission[currentSchedule[i]][0];
			totaltime=totaltime-currentOneAndTwoTime[num];
		}
	}	

	public static void swap(int[] arr,int i,int j){
		int temp = arr[i];
		arr[i] = arr[j];
		arr[j] = temp;
	}
}
1


  测试结果如下:

最佳调度方案为:
1 3 2
其完成时间为18

2.4 背包问题
  该问题的描述是:有n种可选物品1,…,n ,放入容量为c的背包内,使装入的物品具有最大价值。
  该问题和之前的两个问题是类似的,并且更像是装载问题的第一步(装载第一艘船的过程)。解该问题需要的变量:

/*价值数组*/
	private static int[] values = {10,5,20,2,14,23};
	/*重量数组*/
	private static int[] weights ={15,25,40,20,15,24};
	/*背包承重量*/
	private static int capcity = 40;
	/*物品个数*/
	private static int count = weights.length;
	/*当前背包中物品的重量*/
	private static int currentWeight = 0;
	/*当前背包中物品的价值*/
	private static int currentValue = 0;
	/*目前最优装载的价值*/
	static int bestValue = 0;
	/*未装入背包的物品价值*/
	private static int leftValue;
	/*当前解*/
	private static int[] currentResult = new int[count];
	/*最终解*/
	private static int[] bestResult = new int [count];


  values数组代表每件物品的价值;weights数组代表每件物品的重量;capcity代表背包承重量;count代表物品个数;currentWeight代表目前已经装入背包中的物品的总重量,当该变量>背包承重量时,就需要进行回溯操作;currentValue代表已装入背包中物品的价值;bestValue代表目前最优装入方法的物品的总价值;leftValue代表未装入物品的价值;currentResult数组代表当前解,用0和1表示某物品是否装入;bestResult代表最优解,即所有当前解中最优的解。
  基于上面的知识铺垫,我们就可以推导出用递归方法解决背包问题的方法:使用回溯法装背包,当背包中物品的重量<=背包承载量时,继续装载,否则回溯。示例代码如下:

package Recall;
/*有n种可选物品1,…,n ,放入容量为c的背包内,使装入的物品具有最大价值*/
public class ZeroOnePackage {
	/*价值数组*/
	private static int[] values = {10,5,20,2,14,23};
	/*重量数组*/
	private static int[] weights ={15,25,40,20,15,24};
	/*背包承重量*/
	private static int capcity = 40;
	/*物品个数*/
	private static int count = weights.length;
	/*当前背包中物品的重量*/
	private static int currentWeight = 0;
	/*当前背包中物品的价值*/
	private static int currentValue = 0;
	/*目前最优装载的价值*/
	static int bestValue = 0;
	/*未装入背包的物品价值*/
	private static int leftValue;
	/*当前解*/
	private static int[] currentResult = new int[count];
	/*最终解*/
	private static int[] bestResult = new int [count];

	public static void main(String[] args) {
	    /*初始化leftValue,即剩余价值的初始默认值是最开始所有背包的价值总和*/
	    for(int i = 0;i < count; i++) {
	    	leftValue += values[i];
	    }
	    //调用回溯法计算
	    findMaxValue(0);

	    System.out.println("最优装载价值为:" + bestValue);
	    for(int i = 0;i < count;i++) {
	    	if(bestResult[i] == 1)
	    		System.out.println("装载第"+(i+1)+"件物品,这件物品的价值是:"+values[i]);
	    }           
	}

	public static void findMaxValue(int num) {


/*到达叶结点,即已经遍历到序列汇总最后一个值,需要比较当前解与之前存储最优解的关系,然后结束此次遍历*/

if(num == count) {
	        if(currentValue > bestValue) {
	            for(int i = 0;i < count; i++) {
	            	bestResult[i] = currentResult[i];
	            }
	            bestValue = currentValue;
	        }
	        return;
	    }	    /*开始尝试装载第num+1个物品,leftValue需要减去当前物品value*/
	    leftValue = leftValue - values[num];

	    if(currentWeight + weights[num] <= capcity) {   	
	    	/*能装下该物品的话, 标识当前解数组中该物品的值为1,然后currentValue加入当前物品的value
	    	 *currentWeight加入当前物品的重量*/
	    	currentResult[num] = 1;
	        currentValue = currentValue +values[num];
	        currentWeight = currentWeight +weights[num];
	        /*回溯*/
	        findMaxValue(num+1);
	        /*在currentValue和currentWeight减去当前物品的影响,回溯现场*/
	        currentValue = currentValue -values[num];
	        currentWeight = currentWeight -weights[num];

	    }

	    /*恢复现场*/
	    currentResult[num] = 0;
	    findMaxValue(num+1);
	    leftValue += weights[num];
	}
}
1



  测试结果:

最优装载价值为:37
装载第5件物品,这件物品的价值是:14
装载第6件物品,这件物品的价值是:23