01背包问题

        已知一个背包最多能容纳体积之和为v的物品,现有 n 个物品,第 i 个物品的体积为 vi , 价值为 wi,求如何装入物品,使背包中物品价值之和最大?
        01背包问题使用动态规划来求解,那么需要知道dp数组的含义。dp[i][j]表示的含义就是背包容量为j,从0到i个物品中选取部分物品放入背包所对应的最大价值。
        对于物品i,有两种选择,放入和不放入,不放入dp[i][j] = dp[i - 1][j],背包容量为j,从0到i个物品中选取其中物品放入背包所对应的最大价值与背包容量为j,从0到i - 1个物品中选取其中物品放入背包所对应的最大价值是相同的,因为没有放入物品i。放入则dp[i][j] = dp[i - 1][j - 物品i的体积] + 物品i的价值,容量j需要减去放入物品i的体积,同时加上物品i的价值。dp[i - 1][j - 物品i的体积]表示剩余背包容量,从物品0到i-1个物品中选取其中物品放入背包得到的最大价值。因此递推公式为dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - 物品i的体积] + 物品i的价值)
        设背包容量为5,物品0的大小为2,价值为10;物品1的大小为3,价值为15;物品2的大小为4,价值为20,使用二维数组vw保存物品的大小和价值。vw[0][0] = 2,vw[0][1] = 10;vw[1][0] = 3,vw[1][1] = 15;vw[2][0] = 4,vw[2][1] = 20。
        上述例子中背包容量为5,物品数量为3,因此定义dp[5][3],根据递归公式,dp[i][j]依赖于dp[i - 1]和dp[i - 1][j - 物品体积],就是依赖于其上一行的两个数据,因此需要对dp[0]行的数组进行初始化。根据dp数组的含义,dp[0][j]就是背包容量为j,仅有一个物品0,对应的最大价值。显然如果容量j大于等于物品0的体积,背包的最大价值就是物品0的价值,否则,最大价值为0。因此dp数组初始化代码如下:

for(int j = 0;j <= 5;j++){
	if(j >= vw[0][0]){
		dp[0][j] = vw[0][1];
	}
}

        此时dp数组如下表所示,背包容量大于等于物品0的体积时,最大价值就是10,因为背包中物品只能放入一次,且仅有物品0。
在这里插入图片描述

        接着根据递推公式遍历剩余物品。

for(int i = 1;i < 3;i++){
    for(int j = 0;j <= 5;j++){
        if(j >= vw[i][0]){
            dp[i][j] = Math.max(dp[i - 1][j], dp[i - 1][j - vw[i][0]] + vw[i][1]);
        }else{
        	dp[i][j] = dp[i - 1][j];
        }
    }
}

        遍历第二个物品,第二个物品的大小为3,价值为15,j等于0,1,2,即背包容量为0,1,2时都无法放入大小为3的物品,不会进入if,进入else,不考虑放入物品1的情况,当背包容量为3,最大价值为不放入物品1的价值,和放入物品1的价值取最大值,不放入物品1,最大价值为dp[0][3],即仅放入一个物品0的价值为10,放入物品1,则背包容量为j - vw[i][0],就是3 - 3等于0,已无法放入其他物品,dp[0][0],剩余容量为0,仅有物品0,背包最大价值,查上图表,可以得dp[0][0]等于0,因此价值为15,最后取最大价值15。当背包容量为4时,最大价值也是15,因为不放入物品1,则仅有物品0,价值为10,放入物品1,则背包容量为j - vw[i][0],4 - 3 = 1,无法放入物品0。当背包容量为5时,背包容量为j - vw[i][0],5 - 3 = 2,dp[0][2],表示剩余容量2,仅有物品0的最大价值,查上述表得10,即刚好放入物品0和物品1,最大价值为25。遍历完物品2,dp数组如下:
在这里插入图片描述
        遍历第三个物品,当容量j等于0,1,2,3时,背包容量都小于物品2的大小,因此不考虑放入物品2的情况。当背包容量为4时,放入物品2,背包容量为0,dp[1][0],剩余容量为0,有物品0和1,背包最大价值为0,因此价值为20,不放入物品2,价值为15,取最大价值为20,当背包容量为5时,放入物品2,背包剩余容量为1,无法放入其他物品,价值为20,和不放入物品2,价值为dp[1][5],25,取最大价值为25。遍历完最后一个物品,dp数组如下:
在这里插入图片描述
        最后给出01背包的完整代码。

import java.util.*;


public class Solution {
    /**
     * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
     *
     * 计算01背包问题的结果
     * @param V int整型 背包的体积
     * @param n int整型 物品的个数
     * @param vw int整型二维数组 第一维度为n,第二维度为2的二维数组,vw[i][0],vw[i][1]分别描述i+1个物品的vi,wi
     * @return int整型
     */
    public int knapsack (int V, int n, int[][] vw) {
        int[][] dp = new int[n][V + 1];
        for(int j = 0;j <= V;j++){
            if(j >= vw[0][0]){
                dp[0][j] = vw[0][1];
            }
        }  
        for(int i = 1;i < n;i++){
            for(int j = 0;j <= V;j++){
                if(j >= vw[i][0]){
                    dp[i][j] = Math.max(dp[i - 1][j], dp[i - 1][j - vw[i][0]] + vw[i][1]);
                }else{
                    dp[i][j] = dp[i - 1][j];
                }
            }
        }
        return dp[n - 1][V];
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值