01背包问题是背包问题中最经典的一种。
01背包问题是背包问题中最经典的一种。
提示:以下是本篇文章正文内容,下面案例可供参考
一、动态规划
动态规划其实就是,给定一个问题,我们把它拆成一个个子问题,直到子问题可以直接解决。然后呢,把子问题答案保存起来,以减少重复计算。再根据子问题答案反推,得出原问题解的一种方法。
与分治法不同的是,适用于动态规划求解的问题,经分解得到子问题往往不是相互独立的。(即下一个子阶段的求解是建立在上一个子阶段的解的基础上,进行进一步求解)。动态规划可以通过填表的方式来逐步推进,得到最优解。
二、01背包问题
1.问题描述
我们会给N个物品和一个容量为V的背包,每一件物品都有对应的价值v和重量w且每件物品最多只能放置一个不能重复放,问怎么放置能够使不超过背包容量且达到最大价值。
背包问题:有一个背包,容量为5磅,现有如下物品:
物品 重量 价值
1 1 2
2 2 4
3 3 4
4 4 5
(1)要求达到的目标为装入的背包的总价值最大,并且重量不超出
(2)要求装入的物品不能重复
2.算法思路
算法的主要思想,利用动态规划来解决。每次遍历到的第i件物品,根据w[i]和v[i]来确定是否需要将
该物品放入背包中。即对于给定的n个物品,设v[i]、w[i]分别为第i个物品的价值和重量,c为背包的
容量。再令dp[i][j]表示在前i个物品中能够装入容量为j的背包中的最大价值,则我们有下面的结果:
(1) dp[i][0]=dp[0][j]=0。
(2) 当w[i]>j时:dp[i][j]=dp[i-1][j] 当准备加入新增的商品它的容量大于当前背包的容量时就直接使用上一个单元格的值,这种就是你要添加得新物品重量太大,背包不够用,所有相当于没添加,直接用上一个单元的最大值。
(3) 当j>=w[i]时:dp[i][j]=max{dp[i-1][j],dp[i-1][j-w[i]]+v[i]}(注:当准备加入的新增的商品的容量小于等于背包的容量装入的方式:dp[i-1][j]:就是上一个单元格的装入的最大策略dp[i]:表示当前商品的价值 dp[i-1][j-w[i]]:装入i-1商品,到剩余空间j-w[i])。
这种要分两种情况:
不拿:这种情况就是没添加新物品,还是用上一个单元格得最优解。
拿:就是相当于先相当于先不添加这种物品得价值,先找到没添加这一种物品且把抛出这种物品得背包容量得最优解+这种物品得价值最后这两个值进行比较,选取最大值。
图2.1状态转移模拟过程
三、代码
1.代码如下(示例):
package AcWing;
import java.io.*;
public class O一背包问题 {
static PrintWriter pw = new PrintWriter(new OutputStreamWriter(System.out));
static BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
static StreamTokenizer st = new StreamTokenizer(new BufferedReader(new InputStreamReader(System.in)));
public static void main(String[] args) throws Exception{
int n = nextInt();
int bag = nextInt();
//体积
int[] w = new int[n+1];
//价值
int[] v = new int[n+1];
for(int i = 1;i <= n;i++){
w[i] = nextInt();
v[i] = nextInt();
}
//表示当背包容量为j时的最大价值
int[][] dp = new int[n+1][bag+1];
//i表示第几个物品
for(int i = 1; i <= n;i++){
//j表示当前背包的最大容量
for(int j = 1; j <= bag;j++){
//物品容量大于背包容量,那么我们就不加入新物品
if(w[i] > j){
dp[i][j] = dp[i-1][j];
}else {
dp[i][j] = Math.max(dp[i-1][j],dp[i-1][j-w[i]]+v[i]);
}
}
}
pw.println(dp[n][bag]);
pw.flush();
}
public static int nextInt()throws Exception{
st.nextToken();
return (int)st.nval;
}
public static String nextLine()throws Exception{
return br.readLine();
}
}
2.读入数据
4 5
1 2
2 4
3 4
4 5
3.代码运行结果
8
8+4+4(即用第2个物品和第4个物品)
总结
对于01背包问题我们最重要要搞明白状态转移数组dp,知道行和列表达的意思,知道dp[i][j]表达的意思,以及明白状态转移方程是如何得到的。