回溯法:使用java实现01背包问题

网上关于回溯法的资料不多,所以我将课堂所学写下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的多项式

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值