[算法]2道经典背包问题(动规入门)

背包问题

下列实现均为朴素解法,普通但是容易理解!先学会走再学跳嘛!

0-1背包

描述

有N种物品和一个容量为V的背包,每种物品只能使用一次。
第 i 种物品的体积为vi,价值为wi。
问如何放置这些物品,在不超过背包容量的前提下,使得背包内物品价值达到最大值

样例
输入:
	4 5
	1 2
	2 4
	3 4
	4 5
输出:
	 8

原题链接


思路
解析方法

分析状态,推导状态转移方程

1.分析状态时,先分析问题的集合和属性

  • 集合:满足“条件”的所以集合
  • 属性:最值/数量…
  • 状态=属性+集合

2.推导状态转移方程,即如何划分上述的集合


解析过程

状态:f[i][j]
f[i][j] 表示 :所有满足 在前 i-1 种物品中选择,且总体积不超过 j 的方案的最大值。

状态转移方程
在0-1背包问题中,每种物品只能使用一次,所以集合划分就是选还是不选的问题。

在这里插入图片描述
综上所述,状态转移方程为:

当前物品i 的体积 > 背包剩余容量==不选择第 i 种物品

j< v[i] : 
f[i][j]=f[i-1][j]

当物品i的体积<背包剩余容量:
需要在选择 i 和不选 i 物品集合中,找出可以使价值达到最大的方案。
(也就是说:在众多选择方案中,基于之前的选择,我在选了a 和不选 a 的两种情况下,哪一种可以我背包中的物品价值更大)

 j>=v[i] : 
 f[i][j]=max{f[i-1][j],f[i-1][j-v]+w}
实现
import java.util.Scanner;
public class Main{
    /**
     * 问题:问怎样放置物品在不超过背包容量的情况下,达到商品价值的最大值
     * 背包问题:每个物品只能使用一次
     * 状态 dp[i][j]:表示 所有前i个物品,总体积<= j的方案的最大值
     * 状态转移:
     *  不选择第i种物品:dp[i][j]=dp[i-1][j]
     *  (不选择第i种物品时,当前状态下的最大值仍然是前i-1种商品在总体积不超过j的所有方案中的最大值)
     *  选择第i种物品:dp[i][j]=dp[i-1][j-v[i]]+w[i]
     *  (选择第i种物品的方案中最大值=前i-1种物品,放在容量为j-v[i]的背包中的最大值+第i件物品的价值)
     *  状态转移方程
     *  v[i]<=j时,dp[i][j]=max{dp[i-1][j],dp[i-1][j-v[i]]+w[i]}
     *  v[i]>j时, dp[i][j]=dp[i-1][j] (当前物品的添加超出总体积约束范围,不选该物品)
     *  状态初始化
     *  dp[0][0]=0:当在背包不放任何物品时(体积总和为0),背包物品价值(最大值)为0
     */

    public static void main(String[] args) {
        Scanner sc=new Scanner(System.in);
        int N=sc.nextInt();
        int V=sc.nextInt();

        //一个长度为N的数组,第i个元素表示第i个物品的体积、价值
        int[] v=new int[N+1];
        int[] w=new int[N+1];
        for (int i = 1; i <= N; i++) {
            v[i]=sc.nextInt();
            w[i]=sc.nextInt();
        }
        // N+1,V+1 表示:
        // 第0行表示只能选择第0个物品的时候,即没有物品的时候
        // 第0列表示背包的体积为0的时候,即不能装任何东西的时候
        int[][] dp=new int[N+1][V+1];
        //状态初始化
        dp[0][0]=0;
        //枚举各种情况,找出符合条件的最大值
        for (int i = 1; i <= N; i++) {
            for (int j = 0; j <=V; j++) {
                if (j>=v[i]){
                    dp[i][j]=Math.max(dp[i-1][j],dp[i-1][j-v[i]]+w[i]);
                }else {
                    dp[i][j]=dp[i-1][j];
                }
            }
        }
		//最后dp[N][V]就是题目中要求的答案!
        System.out.println(dp[N][V]);
    }
}

完全背包

有N种物品和一个容量为V的背包,每种物品都可以无限次使用。
第i种物品的体积为vi,价值为wi。
问如何放置这些物品,在不超过背包容量的前提下,使得背包内物品价值达到最大值

样例
输入:
4 5
1 2
2 4
3 4
4 5
输出:
10

原题链接

思路

按照0-1背包的分析方法,我们再来看完全背包问题

完全背包和0-1背包在题目描述上的唯一区别:物品可以无限次使用
从而可以判断,在划分集合时,会有所不同。

状态:f[i][j]
所有满足 在前 i-1 种物品中选择,且总体积不超过 j 的方案的最大值。
状态转移方程:
在这里插入图片描述

实现
public class Main{
    /**
     * 过程分析:
     * 状态
     *      集合:所有满足在前i的物品中,物品总价值<=背包容量j的方案的集合
     *      属性:所有满足条件集合的最大值
     * 状态方程
     *      当第i个物品使用0次(就是不往包里装i物品),dp[i][j]=dp[i-1][j]
     *      当第i个物品使用K次,dp[i][j]=dp[i-1][j-k*v[i]]+k*w[i]
     *
     *     dp[i][j]=max{dp[i-1][j],dp[i-1][j-v]+w,dp[i-1][j-2v]+2w....}
     *     dp[i][j-v]=max{dp[i-1][j-v],dp[i-1][j-2v]+w,dp[i-1][j-3v]+2w....}
     *     dp[i][j]=max{dp[i-1][j],dp[i][j-v]+w}
     */
    public static void main(String[] args){
        Scanner sc=new Scanner(System.in);
        int N=sc.nextInt();
        int V=sc.nextInt();
        int[] v=new int[N+1];
        int[] w=new int[N+1];
        for(int i=1;i<=N;i++){
            v[i]=sc.nextInt();
            w[i]=sc.nextInt();
        }
        int[][] dp=new int[N+1][V+1];
        for(int i=1;i<=N;i++){
            for(int j=0;j<=V;j++){
                dp[i][j]=dp[i-1][j];
                if(j>=v[i]){
                    dp[i][j]=Math.max(dp[i-1][j],dp[i][j-v[i]]+w[i]);
                }
            }
        }
        System.out.println(dp[N][V]);
    }
}

推荐文章:背包九讲

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值