动态规划-01背包问题

#问题背景
背包问题:有一个背包,容量为4磅,现有如下物品

物品重量价格
吉他11500
音响43000
电脑32000

目标:
1)要求达到的目标为装入的背包的总价值最大,并且重量不超出背包的容量
2)要求装入的物品不能重复

思路分析

物品重量 : w [ i ] = 1 , 4 , 3 w[i] = {1,4,3} w[i]=1,4,3
物品价值: v a l [ i ] = 1500 , 3000 , 2000 val[i]={1500,3000,2000} val[i]=1500,3000,2000

0磅1磅2磅3磅4磅
0物00000
1号(1磅,1500)01500150015001500
2号(4磅,3000)01500150015003000
3号(3磅,2000)01500150020003500

由图可知,最大的路径是存放1号和3号
设价值矩阵为 v [ i ] [ j ] , v [ i ] [ j ] v[i][j],v[i][j] v[i][j],v[i][j]表示在背包容量为j的情况下,放第i个物品时的最大价值
如果 w [ i ] < j , v [ i ] [ j ] = v [ i − 1 ] [ j ] w[i]<j,v[i][j] = v[i-1][j] w[i]<j,v[i][j]=v[i1][j]
否则 w [ i ] > = j , v [ i ] [ j ] = m a x ( v a l [ i ] + v [ i − 1 ] [ j − w [ i ] ] , v [ i − 1 ] [ j ] ) w[i]>=j,v[i][j] = max(val[i]+v[i-1][j-w[i]],v[i-1][j]) w[i]>=j,v[i][j]=max(val[i]+v[i1][jw[i]]v[i1][j])
如果当前物品的重量 > > >背包的重量,则当前物品放不下,此时最大价值和前一个物品的最大价值相同,即 v [ i ] [ j ] = v [ i − 1 ] [ j ] v[i][j]=v[i-1][j] v[i][j]=v[i1][j]
如果当前物品的重量 < = <= <=背包的重量,则当前物品可以放下。所以可以放该物品也可以不放,
如果不放该物品,此时的价值与前一个物品的价值相同为 v [ i − 1 ] [ j ] v[i-1][j] v[i1][j]
如果放该物品,此时的价值为当前的物品价值+背包容量减去当前物品的重量的情况下的能放下前 i − 1 i-1 i1个物品的最大值,即 v a l [ i ] + v [ i − 1 ] [ j − w [ i ] ] val[i]+v[i-1][j-w[i]] val[i]+v[i1][jw[i]].
所以,应该取两者之间的较大值
w [ i ] > = j , v [ i ] [ j ] = m a x ( v a l [ i ] + v [ i ] [ j − w [ i ] ] , v [ i − 1 ] [ j ] ) w[i]>=j,v[i][j] = max(val[i]+v[i][j-w[i]],v[i-1][j]) w[i]>=j,v[i][j]=max(val[i]+v[i][jw[i]]v[i1][j])

java 代码

// An highlighted block
package dynamic;

public class knapsackProblem {

    public static void main(String[] args) {
        // TODO
        int[] w = {1, 4, 3};//物品的重量
        int[] val = {1500,3000,2000};//物品的价值
        int m =4;//背包的容量
        int n =val.length;//物品的个数

        //创建二维数组:
        // v[i]v[j] 表示前i个物品中能够装入容量为j的背包中的最大价值
        int[][] v = new int[n+1][m+1];

        //为了记录放入商品的情况,我们定义一个二维数组
        int[][] path = new int[n+1][m+1];

        //初始化第一行和第一列,这里在本程序中,可以不去处理,默认是0
        for(int i=0;i<v.length;i++){
            v[i][0] = 0;//这里将第一列设置为0,包的容量为0时,包的价值都是0
        }
        for(int i=0;i<v[0].length;i++){
            v[0][i] = 0;//将第一行设置0,物品的数量为0时,包的价值都是0
        }
        //根据前面得到公式来动态规划处理
        for(int i=1;i<v.length;i++){ //不处理第一行,i是从1开始的
            for(int j=1;j<v[0].length;j++){ //不处理第一列,j是从1开始的
                //公式
                if(w[i-1] >j){ //因为我们的程序是从1开始的,因为原来公式的w[i]修改成w[i-1]
                    v[i][j]= v[i-1][j];
                }else{
                    //说明:
                    //因为我们的i从1开始的,因此公式需要调整
                    //v[i][j]
                    //为了记录商品存放到背包的情况,我们需要用if-else来体现公式
                    if(v[i-1][j] < val[i-1]+v[i-1][j-w[i-1]]){
                        v[i][j] = val[i-1]+v[i-1][j-w[i-1]];
                        //把当前的情况记录到path
                        path[i][j] = 1;
                    }else{
                        v[i][j] = v[i-1][j];
                    }
                }
            }
        }

        //
        int i =path.length -1; //行的最大下标
        int j = path[0].length-1; //列的最大下标
        while(i>0 && j>0){ //从path的最后开始找
            if(path[i][j] ==1){
                System.out.printf("第%d个商品放入到背包\n",i);
                j -=w[i-1];
            }
            i--;
        }

        //输出一下v,看看当前的情况
        for(int l=0;l<v.length;l++){
            for(int k=0;k<v[l].length;k++){
                System.out.print(v[l][k] + " ");
            }
            System.out.println();
        }
    }
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值