背包问题——Java算法求解

背包问题描述:

背包的容量为V,现在有N类物品,第i类物品的重量是weight[i],价值是value[i],那么往该背包里装东西,怎样装才能使得最终包内物品的总价值最大。这里装物品主要由三种装法:
1、0-1背包:每类物品最多只能装一次
2、多重背包:每类物品都有个数限制,第i类物品最多可以装num[i]次
3、完全背包:每类物品可以无限次装进包内
 

采用动态规划方法求解:

用dp[N][V]来存储中间状态值,dp[i][j]表示前i件物品能装入容量为j的物品总价值最大,所以最终的结果值是dp[N][V]。现在需要求状态转移条件。假如现在已经求解出了前i-1件物品装入容量j的背包价值最大,因为固定容量j不变,所以对即将装入的i件物品,有如下讨论:

  • 1)如果第i件物品的重量weight[i] > j,表示不能装入,所以dp[i][j] = dp[i-1][j];
  • 2)如果第i件物品的重量weight[i] <= j, 择优dp[i][j] = dp[i-1][j-weight[i]]+value[i];

因为是要求最大的价值,所以如果装了第i件物品后的总价值dp[i-1][j-weight[i]]+value[i]>大于之前的总价值dp[i-1][j],则肯是最大的;反之则说明第i件物品不必装入容量为j的背包。状态转移方程就是:

dp[i][j] = Math.max(dp[i-1][j], dp[i-1][j-weight[i]]+value[i])

Java代码实现

public static String pack(int N, int V, int[]weight, int[] value){
    // 为了方便,我们按照正常的1开始,所以中间变量会多于一个,第0个都是0
    int[][]dp = new int[N+1][V+1];
    for(int i=1; i<N+1; i++){
        for(j=1; j<V+1;j++){
            if(weight[i-1] > j){
                dp[i][j] = dp[i-1][j];
            }else{
                dp[i][j] = Math.max(dp[i-1][j], dp[i-1][j-weight[i-1]]+value[i-1]);
            }
        }

        int maxValue =  dp[N][V];

        // 利用反向回溯的方式,得到装入物品的编号
        int j = V;
        String str = " ";
        for(int i = N; i>0;i--){
            if(dp[i][j] > dp[i-1][j]){
                str = i + " " + str;
                j = j - weight[i];
            }
            if(j == 0){
                break;
            }
        }
        return str;
    }
}

第二类问题,假设物品的数量是有限,并不是唯一一个,所以问题的解决方式主要在状态转移的时候,考虑数量的限制;

public static String pack(int N, int V, int[]weight, int[] value, int[] nums){
    // 为了方便,我们按照正常的1开始,所以中间变量会多于一个,第0个都是0
    int[][]dp = new int[N+1][V+1];
    for(int i=1; i<N+1; i++){
        for(j=1; j<V+1;j++){
            if(weight[i-1] > j){
                dp[i][j] = dp[i-1][j];
            }else{
                //考虑物品的件数限制
                int vMax = Math.min(nums[i-1], j/weight[i-1]);
                for(int k =0; k<= vMax; k++){
                    dp[i][j] = Math.max(dp[i-1][j], dp[i-1][j-k*weight[i-1]]+k*value[i-1]);
                }
            }
        }

        int maxValue =  dp[N][V];

        // 利用反向回溯的方式,得到装入物品的编号
        int j = V;
        String str = " ";
        for(int i = N; i>0;i--){
            //若果dp[i][j]>dp[i-1][j],这说明第i件物品是放入背包的
            while(dp[i][j] > dp[i-1][j]){
                str = i + " " + str;
                j = j - weight[i-1];
            }
            if(j == 0){
                break;
            }
        }
        return str;
    }
}

完全背包问题:不考虑物品数量的限制,所以相同的物品可以无限制的放入其中,所以状态转移的时候,有变化的是:

f[i][y] = max{f[i-1][y], (f[i][y-weight[i]]+value[i])},

Java实现是:

public static String pack(int N, int V, int[]weight, int[] value){
    // 为了方便,我们按照正常的1开始,所以中间变量会多于一个,第0个都是0
    int[][]dp = new int[N+1][V+1];
    for(int i=1; i<N+1; i++){
        for(j=1; j<V+1;j++){
            if(weight[i-1] > j){
                dp[i][j] = dp[i-1][j];
            }else{
                // 同一类物品可以多次无限制放入
                dp[i][j] = Math.max(dp[i-1][j], dp[i][j-weight[i-1]]+value[i-1]);
            }
        }

        int maxValue =  dp[N][V];

        // 利用反向回溯的方式,得到装入物品的编号
        int j = V;
        String str = " ";
        for(int i = N; i>0;i--){
            //若果dp[i][j]>dp[i-1][j],这说明第i件物品是放入背包的
            while(dp[i][j] > dp[i-1][j]){
                str = i + " " + str;
                j = j - weight[i-1];
            }
            if(j == 0){
                break;
            }
        }
        return str;
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值