01背包问题Java(动态规划和暴力求解)

比较经典的一个问题,在理清思路之后,写出了代码。动态规划的思想还是将大的问题分解成小的的问题。或者说是分成小的步骤。例如在背包问题中,想求解背包容量为N的最大价值,先求解容量为N-1的最大价值,一步一步向前推,最终在返回来。自己也是理解了好久,突然有一天早上刷牙,就把问题想明白了。还是要多悟,多说无益,看代码:

package com.zxg.algorithm.PackageQuestion;

/**
 * 01背包问题 将重量为(w1,w2,w3,w4,w5...)、价值为(v1,v2,v3,v4,v5...)的物品放入容量为N的背包中,怎样放价值最大
 * 动态规划:
 * 我们设v[i][j]为放入前i个物品到容量为j的背包中的最大值,i一定是小于物品总个数的,j一定是小于N的
 * 举个例子 v[2][5]表示将前两个物品放入容量为5的背包中的最大价值 v[2][6]表示将前两个物品放入容量为6的背包中的最大价值
 * 则有等式:
 * v[0][j] = v[j][0] = 0
 * v[i][j] = v[i-1][j] w[i]>j
 * v[i][j] = Max(v[i-1][j],v[i-1][j-w[i]]+value[i]) w[i] <= j
 * 暴力求解:
 * 求出物品个数的所有子集,计算每一个子集的价值,选出最大值
 * 例如有4个物品,那么2^4=16个子集,每个子集有四位,每一位是0代表该子集中没有该物品,是1代表有该物品
 * 0 0 0 0 代表该子集中没有任何物品
 * 0 1 0 1 代表该子集中有2号物品和4号物品
 */
public class Package_01 {

    /**
     * 动态规划
     * @param weight
     * @param value
     * @param capacity
     * @return
     */
    public static int maxValue(int[] weight, int[] value, int capacity) {
        int weightLen = weight.length;
        int valueLen = capacity + 1;//列值长度加1,是因为最后一列要保证重量值为lenColumn
        int maxValue = 0;
        int[][] v = new int[weightLen][valueLen];
        for (int i = 0; i < weightLen; i++) {
            for (int j = 0; j < valueLen; j++) {
                if (i == 0) {
                    v[i][j] = 0;
                } else if (j == 0) {
                    v[i][j] = 0;
                } else {
                    if (weight[i] > j) {
                        v[i][j] = v[i - 1][j];

                    } else if (weight[i] <= j) {
                        v[i][j] = Math.max(v[i - 1][j], v[i - 1][j - weight[i]] + value[i]);
                    }
                    maxValue = v[i][j];
                }

            }

        }
        return maxValue;
    }

    /**
     * 暴力求解
     * @param weight
     * @param value
     * @param capacity
     * @return
     */
    public static int maxValueForce(int[] weight,int[] value,int capacity){
        //子集个数
        int row = (int)Math.pow(2,weight.length);
        //每个子集中元素个数,就是物品的个数
        int column = weight.length;
        //物品子集
        int[][] goodsSubset = new int[row][column];
        //最大价值
        int maxValue = 0;
        //填充所有子集
        for(int i=0;i<row;i++){
            int temp_1 = i;
            for(int j=0;j<column;j++){
                int temp_2 = temp_1%2;
                goodsSubset[i][j] = temp_2;
                temp_1 = temp_1/2;
            }
        }
        //遍历子集,为每一个子集计算总价值,输出总价值最大的子集
        for(int i=0;i<goodsSubset.length;i++){
            int tempWeight = 0;
            int tempValue = 0;
            for(int j=0;j<goodsSubset[i].length;j++){
                System.out.printf(goodsSubset[i][j]+" ");
                tempWeight += goodsSubset[i][j]*weight[j];
                tempValue += goodsSubset[i][j]*value[j];
            }
            System.out.print("\t"+"总重量为:"+ tempWeight);
            if(tempWeight <= capacity){
                System.out.printf("\t"+"总价值为:"+tempValue);
            }else {
                System.out.printf("\t"+"不可行,超出背包最大承重");
            }
            if(tempWeight <= capacity && tempValue>maxValue){
                maxValue = tempValue;
            }
            System.out.println();
        }
        System.out.println("最大值:"+maxValue);
        return maxValue;
    }

    public static void main(String[] args) {
//        int[] weight = {1, 3, 5, 7, 1};
//        int[] value = {2, 4, 3, 6, 3};
        int[] weight = {7,3,4,5};
        int[] value = {42,12,40,25};
        int capacity = 10; //容量
        System.out.println(maxValue(weight, value, capacity));
    }

}

参考:https://www.cnblogs.com/liuzhen1995/p/6374541.html#a2.3

           https://blog.csdn.net/ls5718/article/details/52227908

           https://www.cnblogs.com/Christal-R/p/Dynamic_programming.html     

这里再多说一句,为什么不能使用贪心算法思路来解决,因为使用贪心算法的前提是无后效性,即某个状态以后的过程不会影响以前的状态,只与当前状态有关。但是放在该问题中不适用,因为你在当前选择了一个物品,就增加了背包相应的重量,那么剩余的重量就会减少,这对之后再选择物品时时有影响的。 所以对所采用的贪心策略一定要仔细分析其是否满足无后效性。

 

  • 1
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值