背包问题__01背包

01背包

01背包,是指在N个物品中,每个物品的体积为vi[i],每个物品的权重(价值)为wi[i]。怎么在体积为V的背包中,获得最大价值

且在01背包中,每件物品只有两种选法:选或是不选

注:本帖子中的所有内容,均以java代码进行演示

问题一

在这里插入图片描述
点击题目,跳转到原题。

剖析:

用 v[i] 表示每件物品的体积,用 wi[i] 表示每件物品的权重(价值)

​ 用 dp【i】【j】表示,对于第 i 件物品,在空间为j时,获得的最大价值。

​ 因为,第0件物品,在任何空间下的价值都为0,所以可以不用做判断。

​ 则可以从第1件物品,依次进行遍历,直到第 N 件物品时结束。

​ 当前遍历到第 i 件物品时,若背包体积小于vi[i] ,则获得的最大价值与 dp【i-1】【j】相同。当背包的体积大于

vi[i]时,则分为两种情况。

​ 情况一:选择第 i 件物品,则获得的价值为 dp【i-1】【j-vi[i]】+ wi[i]

​ 情况二:不选择第 i 件物品,则获得的价值为 dp【i-1】【j】

​ 由此可以得出01背包的二维数组表示法

说明:因为本题,需要自己主函数、输入、输出函数,故将该部分进行抽取如下:

import java.util.*;
class Main {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        //scanner.next() 标准输出函数
        //物品数量
        int n = scanner.nextInt();
        //最大体积
        int v = scanner.nextInt();
        //第i件物品所占的体积
        int[] vi = new int[n + 1];
        //第i件物品的权重
        int[] wi = new int[n + 1];
        // 接下来有 N 行,每行有两个整数:v[i],w[i],用空格隔开,分别表示第i件物品的体积和价值
        for (int i = 1; i <= n; i++) {
            vi[i] = scanner.nextInt();
            wi[i] = scanner.nextInt();
        }
        //主要实现函数
        oneTwoBackpacker(n, v, vi, wi);
    }
}

01背包的二维数组表示法

//二维数组,表示法
private static void oneTwoBackpacker(int n, int v, int[] vi, int[] wi) {
    int[][] dp = new int[n + 1][v + 1];
    //从第一个物品,到第N个物品
    for (int i = 1; i <= n; i++) {
        //从0体积,到v体积
        for (int j = 0; j <= v; j++) {
            if (j >= vi[i])
                dp[i][j] = Math.max(dp[i - 1][j], dp[i - 1][j - vi[i]] + wi[i]);
            else dp[i][j] = dp[i - 1][j];
        }
    }
    System.out.println(dp[n][v]);
}

我们注意到,在求取 **dp【i】【j】**时,只使用到了 dp【i-1】【j】dp【i-1】【j-vi[i]】,故可以利用滚动数组,对此进行优化。

但是如果直接将 dp 数组,降到一维的话,会出现一个问题,那就是在计算 dp[j] = Math.max(dp[j], dp[j - vi[i]] + wi[i]);时,由于 j-vi[i] 小于 j,且我们的 j,是从小到大,进行遍历的。那就会造成一个问题,dp[j - vi[i]]!=dp[i - 1][j - vi[i]]。因为 dp[i - 1][j - vi[i]],计算的是前 i-1 个物品(包含第 i-1 个物品),在体积为 j - vi[i] 时,所获得的最大价值。而 dp[j - vi[i]],计算的则是前 i 个物品(包含第 i 个物品),在体积为 j - vi[i] 时,所获得的最大价值

所以,我们如果要想使用滚动数组,优化01背包,就要解决这个问题。

其实,如果我们让 j ,从大到小,依次遍历的话,问题就解决了。因为在计算到 dp[j] = Math.max(dp[j], dp[j - vi[i]] + wi[i]); 时,由于 (j - vi[i])<j,故当前用到的还是前i-1个物品的 dp[j - vi[i].

01背包的一维数组表示法

//一维数组优化
private static void oneTwoBackpacker(int n, int v, int[] vi, int[] wi) {
    int[] dp = new int[v + 1];
    //从第一个物品,到第N个物品
    for (int i = 1; i <= n; i++) {
        //从v体积到vi[i]体积
        for (int j = v; j >= vi[i]; j--) {
            dp[j] = Math.max(dp[j], dp[j - vi[i]] + wi[i]);
        }
    }
    System.out.println(dp[v]);
}
感谢您的阅读!
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值