0/1背包问题属于比较典型的算法了,它的关键点在于“拿不拿”。
现在我们有一个容量为M公斤的背包,n件物品,她们的重量分别是W1、W2.......Wn,他们的价值分别为C1、C2.......Cn,求能获得的最大价值。
输入:
第一行:两个整数,M(背包容量,M<=200)和N(物品数量,N<=30);
第2...N+1行:每行输入两个整数Wi,Ci,表示每个物品的重量和价值。
输出:
一个数,表示最大价值。
输入案例:
背包容量和物品个数:10 4
物品质量和价值:
2 1
3 3
4 5
7 9
画表格分析:
关于“拿”还是“不拿”,关键在于背包容量是否允许“拿”,拿了之后的价值是否大于不拿的价值。
DP | ||||||||||||
w[i] | v[i] | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 |
0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
2 | 1 | 0 | 0 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 |
3 | 3 | 0 | 0 | 1 | 3 | 3 | 4 | 4 | 4 | 4 | 4 | 4 |
4 | 5 | 0 | 0 | 1 | 3 | 5 | 5 | 6 | 8 | 8 | 9 | 9 |
7 | 9 | 0 | 0 | 1 | 3 | 5 | 5 | 6 | 9 | 9 | 10 | 12 |
注:其中w[]代表物品的重量,v[]代表物品的价值,第一行的0-10代表背包的容量。
如果背包的容量小于该物品重量,那么只能选择“不拿”,那么
dp[i][j]=dp[i-1][j];
如果背包容量不小于物品质量,那么就需要比较“拿”的价值最大,还是不拿的价值最大。(也许减去w[i]你会有疑问,在下面总代码中会进行解释)
dp[i][j]=Math.max(dp[i-1][j],dp[i-1][j-w[i]]+v[i]);
输出dp数组(也就是上面表格的右部):
最后一个元素12就是最大价值。
总代码:
public class beibao01 {
public static void main(String[] args) {
Scanner sc=new Scanner(System.in);
System.out.println("请分别输入背包的容量和物品的个数:");
int cont=sc.nextInt();
int num=sc.nextInt();
System.out.println("请分别输入背包的物品重量和价值:");
int []w=new int[num+1];
int []v=new int[num+1];
int [][]dp=new int[num+1][cont+1];
for(int i=1;i<=num;i++) {
w[i]=sc.nextInt();//物品重量
v[i]=sc.nextInt();//物品价值
}
for(int i=1;i<=num;i++) {
for(int j=1;j<=cont;j++) {
if(j<w[i]) {
dp[i][j]=dp[i-1][j];
}else {
//dp[i-1][j]是指“不拿”的情况,当前的dp值直接等于上一层的值。
//dp[i-1][j-w[i]]+v[i]指的是“拿”的情况是指上一层的dp的值加上当前物品的价值。
//也许会有个疑问,为什么是dp[i-1][j-w[i]]+v[i]中减去的是w[i],而不是1,因为可能你上一个也没有“拿”,如果-1的话意思就是你上一个一定是“拿”的,如果上一个“不拿”呢?甚至说上上一个“不拿”呢?当你减去当前物品的重量的话,那么剩下的就是上一次“拿”的时候总重量,这样得到的dp才是上一次“拿”的时候的总价值。
dp[i][j]=Math.max(dp[i-1][j], dp[i-1][j-w[i]]+v[i]);
}
}
}
for(int i=0;i<=num;i++) {
for(int j=0;j<=cont;j++) {
System.out.print(dp[i][j]+" ");
}
System.out.println("");
System.out.println("");
}
}
}