以一道蓝桥杯例题,来解说今天的主题——背包问题
题目 1924: 蓝桥杯算法提高VIP-01背包
时间限制: 1Sec 内存限制: 128MB 提交: 4826 解决: 1368
题目描述
给定N个物品,每个物品有一个重量W和一个价值V.你有一个能装M重量的背包.问怎么装使得所装价值最大.每个物品只有一个.
输入
输入的第一行包含两个整数n, m,分别表示物品的个数和背包能装重量。 以后N行每行两个数Wi和Vi,表示物品的重量和价值
输出
输出1行,包含一个整数,表示最大价值。
样例输入复制
3 5 2 3 3 5 4 7
样例输出复制
8
通过阅读完这道题,我们发现这是一道很精单的01背包问题。此题中,我们可以很简单的找到3和5凑成的价值为8是本题的解,但是这是相对于我们“人”而言,能够快速以我们的主观判断能力找解,而电脑机器没有我们这么厉害,它没这么灵活,这是机器和人最本质的区别!
好了,言归正传,针对于背包问题,我们该如何解答呢
如何将这三个物品放入背包呢,当指针指向任意一个物品时(如下图),我们假设此物品的价值为dp【i】【j】(i代表此物品的索引值,这里我们1就是代表第一个物品,2代表第二个物品,以此类推,j代表书包的质量,这里j是从1开始递增的,代表书包在某一个质量所能承载的最大价值),那么当指针指向该物品时,我们将面临两种情况——拿走该物品或者不拿该物品。当不拿走该物品时,dp【i】【j】=dp【i-1】【j】,为什么是这样呢?因为不拿物品的话,书包的质量b根本没变化,所承载的价值仍和上一个物品i-1的价值相同。当拿走该物品时,dp【i】【j】=dp【i-1】【j-W【i】】+V【i】,为什么是这样呢?我们可以拆分理解,当拿走物品时,背包的价值就只是相差V【i】,从dp【i】【j】=dp【i-1】【j-W【i】】+V【i】中可知,dp【i】【j】代表当前背包,那么dp【i-1】【j-W【i】】会不会代表没拿走该物品之前书包的价值?显然是对的,因为i-1代表上一个物品,j-W【i】也是代表拿完上一个物品书包的质量,所以显然dp【i-1】【j-W【i】】是代表没拿走该物品之前书包的价值。
基于此,我们解决此题,可以书写如下代码:
package test;
import java.util.*;
public class Main {
public static void main(String[] args) {
Scanner sc=new Scanner(System.in);
int n=sc.nextInt();int m=sc.nextInt();
int[] Wi=new int[n+1]; //创建Wi数组存储质量
int[] Vi=new int[n+1]; //创建Vi数组存储某个物件所对应的价值
for(int i=1;i<n+1;i++) {
Wi[i]=sc.nextInt();
Vi[i]=sc.nextInt();
}
int[][] dp=new int[n+1][m+1]; //创建dp数组存储背包所承受某个质量对应的价值
for(int i=1;i<n+1;i++) {
for(int j=1;j<m+1;j++) {
if(Wi[i]>j) { //若当前物品的质量大于背包所承载的质量,则“不拿”该物品
dp[i][j]=dp[i-1][j];
}
else { //若当前物品的质量小于背包所承载的质量,则可以选择“拿” 或者 “不拿”,为了让书包价值更大,我们选其中的最大值
dp[i][j]=Math.max(dp[i-1][j], dp[i-1][j-Wi[i]]+Vi[i]);
}
}
}
System.out.println(dp[n][m]); //算法完成后,dp数组中的dp[n][m]是数组遍历完成的最后一个元素,也就是书包的最大价值
}
}