简单0-1背包问题求解

136 阅读2分钟

1、题目描述

小明有一个容量为V的背包。

这天他去商场购物,商场一共有N件物品,第i件物品的体积为wiw_i,价值为viv_i

小明想知道再购买的物品总体积不超过V的情况下所能获得的最大价值为多少,请你帮他算算。

输入描述

输入第1行包含两个正整数N,V,表示商场物品的数量和小明的背包容量。

2N+12\sim N+1行每行包含两个正整数w,v,表示物品的体积和价值。

1N102,1V103,1wi,vi1031\le N\le10^2,1\le V\le10^3,1\le w_i,v_i\le10^3

输出描述

输出一行整数表示小明所能获得的最大价值。

输入输出样例

输入

5 20
1 6
2 5
3 8
5 15
3 3

输出

37

2、示例分析

我们用一个简单的实例去分析,我们假设当前物品描述如下:

物品编号1234
重量2345
价值3458

我们有4件物品,背包容量为8,我们的目标是求在背包容量为8的前提下能装物品的最大价值。

定义f(k,w)为:当背包容量为w,现在有k件物品可以偷,所能偷到的最大价值。

所以我们的目标就是求f(4,8)

分析如下:

image-20230314232712264

上图中带\surd表示选了第k件物品,×\times表示没选第k件物品。

如果拿了第4件物品,那么f(4,8)f(4,8)就转化为拿了前3件物品的情况,此时f(4,8)=f(3,85)+8f(4,8)=f(3,8-5)+8

如果没拿第4件物品,那么f(4,8)=f(3,8)f(4,8)=f(3,8),因为没拿第4件,所以背包容量没变。

得出状态转移方程:

f(k,w)={f(k1,w),wk>w,(太重,装不下)max[f(k1,w),f(k1,wwk)+vk],wk<=wf(k,w)=\left\{\begin{matrix} f(k-1,w),w_k>w,(太重,装不下) \\ max[f(k-1,w),f(k-1,w-w_k)+v_k],w_k<=w \end{matrix}\right.

wk<=ww_k<=w时,f(k1,w)f(k-1,w)代表没拿第k件物品,f(k1,wwk)+vkf(k-1,w-w_k)+v_k代表拿了第k件物品。

我们可以将所有的计算结果写成表格。

物品编号\背包容量012345678
0000000000
1:w=2,v=3003333333
2:w=3,v=4003447777
3:w=4,v=5003457899
4:w=5,v=800345881112

这里f(0,w)表示不拿物品,价值肯定为0,f(k,0)表示被包装量为0,肯定装不下,所以第一行和第一列都是0,这里算几个关键的。

f(1,2)=max[f(0,0)+3,f(0,2)]=max[3,0]=3f(1,2)=max[f(0,0)+3,f(0,2)]=max[3,0]=3

f(2,3)=max[f(1,33)+4,f(1,3)]=max[4,3]=4f(2,3)=max[f(1,3-3)+4,f(1,3)]=max[4,3]=4

f(2,4)=max[f(1,44)+4,f(1,4)]=max(4,3)=4f(2,4)=max[f(1,4-4)+4,f(1,4)]=max(4,3)=4

f(2,5)=max[f(1,53)+4,f(1,5)]=max[f(1,2)+4,f(1,5)]=max[7,3]=7f(2,5)=max[f(1,5-3)+4,f(1,5)]=max[f(1,2)+4,f(1,5)]=max[7,3]=7

这里只给出一部分的计算过程

3、代码实现

import java.util.Scanner;

//小明的背包
public class Bag {
    public static void main(String[] args) {
        Scanner scan = new Scanner(System.in);
        int N = scan.nextInt(); //物品数量
        int V = scan.nextInt(); //背包容量
        int[] w = new int[N + 1];//物品体积
        int[] v = new int[N + 1];//物品价值
        //记录最优解,我们的目标是f[N][V]
        int[][] f = new int[N + 1][V + 1];
        for (int i = 1; i <=N; i++) {
            w[i]=scan.nextInt();
            v[i]=scan.nextInt();
        }
        for (int i = 1; i <=N; i++) {	//i代表N件物品
            for (int j = 1; j <=V; j++) {	//j表示背包容量
                if(w[i]>j){ //太重,装不下
                    f[i][j]=f[i-1][j];
                }else{
                    //f[i][j]=max{不拿第i件,拿第i件}
                    f[i][j]=Math.max(f[i-1][j],f[i-1][j-w[i]]+v[i]);
                }
            }
        }
        System.out.println(f[N][V]);
    }
}

image-20230314235222214