网上关于回溯法的资料不多,所以我将课堂所学写下ppt,感谢学校的ppt,学校logo已经打码
我们要注意的是:使用回溯法进行01背包问题和贪心算法一样,需要按性价比排序,
上界函数:bp=cp+rp;cp:当前选中重量,rp:使用贪心求解出的剩余最大价值
下届函数L:变量L初值为0,遇到一个答案结点便计算该答案结 点的收益值fp,且令L=max{L, fp}
有了以上基础后我们来看一下回溯树
大家可以看出回溯之后会探测该节点的右节点的bp值进行探测要求bp>L
给出伪代码:思想就是这么个思想:废话不多说看是上代码!
package com.ucas.analysis.unite6;
import org.junit.Test;
/*
使用回溯法求解背包问题
1.N= 5, M=12, (p1, p2, …, p5) = (10, 15, 6, 8, 4), (w1, w2, …, w5) = (4, 6, 3, 4, 2) 。
2.N= 5, M=15, (w1, w2, …, w5) = (p1, p2, …, p5) = (4, 4, 5, 8, 9)。
*/
public class homework20BackBag {
//直接在类加载时定义变量,一会就不用传来传去了
int n=5;
//第一题
double m=15;
double[]p={10,15,6,8,4};//价格列表
double[]w={4,6,3,4,2};//重量列表
//第二题
// double m=12;
// double[]p={4,4,5,8,9};//价格列表
// double[]w={4,4,5,8,9};//重量列表
double[]perp=new double[p.length];//单价:回溯算法性价比排序
int[]order={0,1,2,3,4};//物品编号
int[]put={0,0,0,0,0};//设置该物品是否装入,0不装;1装
double cw=0.0;//当前背包重量
double cp=0.0;//当前背包中物品总价值
double L=0.0;//当前最优价值,也就是我们所说的下届函数
@Test
public void test(){
sortDesc();
backBag(0);
System.out.println("最优价值:"+L);
System.out.println("最优put:"+put[0]+","+put[1]+","+put[2]+","+put[3]+","+put[4]);
System.out.println("装入的列表:");
System.out.println("一号物品:"+put[0]+",2号物品:"+put[1]+",3号物品:"+put[2]+",4号物品:"+put[3]+",5号物品:"+put[4]);
}
/**
* 回溯函数:左子树需要满足重量要求:cw<m
* 如果bp<L(下届函数):则杀死当前的整个节点
* @param i 表示达到的层数(从0开始)
*/
void backBag(int i){
double bp=bound(i);
if(bp<L) {//其实这个if是没有用的,我只是强化记忆一下这个bp和L的关系
System.out.println("废弃掉第:" + i+"个背包");
return;
}
if(i>n-1){
L=cp;
return;
}
//若左节点可行:直接搜索左子树
//右子树先计算上届函数,以判断是否将其删去
if(cw+w[i]<=m){//将物品i放入背包,左子树成立
System.out.println("第"+i+"个物品要进入左子树,此时:下届函数L="+L+",上届函数bp="+bp);
cw+=w[i];
cp+=p[i];
put[i]=1;//put数组当前的i放成1
backBag(i+1);//向下深层搜索
System.out.println("第"+i+"级计算完毕,开始回溯,此时:下届函数L="+L+",上届函数bp="+bp);
cw-=w[i];//回溯上一层
cp-=p[i];//回溯上一层
}
if(bound(i+1)>L){//进入右子树
put[i]=0;
System.out.println("第"+i+"个物品舍弃掉,进入右子树");
backBag(i+1);
}
}
//计算上界函数
//公式:当前背包总价值:cp+剩余客容纳最大价值(贪心算法求解)<=当前最优值L
double bound(int i){
double leftw=m-cw;//总重-当前重量=剩余重量
double bp=cp;//bp上界函数:bp=cp+rp rp=p[i]*x[i]
//以性价比降序装入物品
while(i<n && w[i]<=leftw){
leftw-=w[i];
bp+=p[i];//bp=cp+rp 把cp加到bp
i++;//复习语法:i=i++: i 虽然自增了但是又被赋值了0 , 这样输出的结果自然就是 0 了
}
if(i<n){
bp+=(p[i]/w[i])*leftw;
}
//System.out.println("第"+i+"个物品的上届函数bp:"+bp);
return bp;
}
//性价比排序倒序:从高到低
public void sortDesc(){
int temporder=0;
double temp=0.0;
for(int i=0;i<n;i++){
perp[i]=p[i]/w[i];
}
for(int i=0;i<n-1;i++){
for (int j=i+1;j<n;j++){
if(perp[i]<perp[j]){
temp=perp[i];
perp[i]=perp[i];
perp[j]=temp;
temporder=order[i];
order[i]=order[j];
order[j]=temporder;
temp = p[i];//冒泡对v[]排序
p[i]=p[j];
p[j]=temp;
temp=w[i];//冒泡对w[]排序
w[i]=w[j];
w[j]=temp;
}
}
}
System.out.println("排序后价值:"+p[0]+","+p[1]+","+p[2]+","+p[3]+","+p[4]);
System.out.println("排序后重量:"+w[0]+","+w[1]+","+w[2]+","+w[3]+","+w[4]);
System.out.println("排序后性价比:"+perp[0]+","+perp[1]+","+perp[2]+","+perp[3]+","+perp[4]);
}
}
根据蒙卡罗特原则:
如果解空间的结点数是2n或者n!
最坏情况时间 O(p(n)2n),p(n)为n的多项式
O(q(n)n!),q(n)为n的多项式