文章目录
一、理论基础
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