背包问题详解

概述

本文是我大二算法分析实验的综合大实验,可能跟大家的比起来难度微不足道,希望能对你有帮助🤪

0/1背包

问题描述:

给定n种物品和一背包。物品i的重量是wi,其价值为vi,背包的容量为C。问应如何选择装入背包的物品,使得装入背包中物品的总价值最大?

对于一种物品,要么装入背包,要么不装。所以对于一种物品的装入状态可以取0和1.我们设物品i的装入状态为xi,xi∈ (0,1),此问题称为0-1背包问题。

1.递归求解

我们用F(n,C)表示将前n个物品放进容量为C的背包里,得到的最大的价值。

我们用自顶向下的角度来看,假如我们已经进行到了最后一步(即求解将n个物品放到背包里获得的最大价值),此时我们便有两种选择

  1. 不放第n个物品,此时总价值为F(n-1,C)
  2. 放置第n个物品,此时总价值为Vn+F(n-1,C-Wn)

两种选择中总价值最大的方案就是我们的最终方案:F(i,C)=max(F(i−1,C),v(i)+F(i−1,C−w(i)))

/**
	 * 递归解决
	 * @param w		物品的重量数组
	 * @param v		物品的价值数组
	 * @param index 当前待选择的物品索引
	 * @param capacity	当前背包有效容量
	 * @return	最大价值
	 */
private static int recursion(int[] w,int[] v,int index,int capacity) {
   
    //基准条件:如果索引无效或者容量不足,直接返回当前价值0
    if(index<0||capacity<=0) {
   
        return 0;
    }
    //不放第index个物品所得价值
    int res = recursion(w, v, index-1, capacity);
    //放第index个物品所得价值(前提是:第index个物品可以放得下)
    if(w[index]<=capacity) {
   
        res = Math.max(res, v[index]+recursion(w, v, index-1, capacity-w[index]));
    }
    return res;
}
public static int knapSack(int[] w, int[] v, int C) {
   
    int size = w.length;
    return solveKS(w, v, size - 1, C);
}

public static void main(String[] args){
   
    int[] w = {
   2,1,3,2};
    int[] v = {
   12,10,20,15};
    System.out.println(knapSack(w,v,5));
}

2.记忆化搜索(自顶向下备忘录法)

我们可以将已经求得的子问题的结果保存下来,这样对子问题只会求解一次

/**
	 * 自顶向下备忘录法
	 * @param w
	 * @param v
	 * @param index
	 * @param capacity
	 * @return
	 */
private static int top2buttom(int[] w,int[] v,int index,int capacity,int[][] memo) {
   
    if(index<0||capacity<=0) {
   
        return 0;
    }
    //如果此子问题已经求解过,则直接返回上次求解的结果
    if(memo[index][capacity]!=0)return memo[index][capacity];
    //不放第index个物品所得价值
    int res = recursion(w, v, index-1, capacity);
    //放第index个物品所得价值(前提是:第index个物品可以放得下)
    if(w[index]<=capacity) {
   
        res = Math.max(res, v[index]+recursion(w, v, index-1, capacity-w[index]));
    }
    //添加子问题的解,便于下次直接使用
    memo[index][capacity] = res;
    return res;
}

3.动态规划算法

通过反证法可证明此问题符合动态规划算法的最优子结构性质,可以通过动态规划来求解。了解动态规划

img

img

img

/**
	 * 动态规划(自底向上)算法
	 * @param w
	 * @param v
	 * @param C
	 * @return
	 */
public static int buttom2top(int[] w,int[] v,int C) {
   
    int size = w.length;
    if(size == 0 || C<=0)return 0;
    int[][] dp = new int[size][C+1];
    //初始化第一行:仅考虑容量为C的背包放第0个物品的情况
    for(int i=0;i<=C;i++) {
   
        dp[0][i] = w[0]<=i? v[0]:0;//如果容量可以放,就是第一个的价值,否则放不了为0
    }
    //根据此逐步填充
    for(int i=1;i<size;i++) {
   //每个物品的循环
        for(int j=0;j<=C;j++) {
   
            dp[i][j] = dp[i-1][j];//要放此物品,说明前面的已经是最优解,根据这个最优解为基准看是增加还是不增加这个物品
            if(w[i]<=j) {
   //如果容量够叫放进去试试看
                dp
  • 4
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值