01背包问题(Java)
题目
有 N 件物品和一个容量是 V 的背包。每件物品只能使用一次。
第 i 件物品的体积是 vi,价值是 wi。
求解将哪些物品装入背包,可使这些物品的总体积不超过背包容量,且总价值最大。
输出最大价值。
输入格式
第一行两个整数,N,V,用空格隔开,分别表示物品数量和背包容积。
接下来有 N 行,每行两个整数 vi,wi,用空格隔开,分别表示第 i 件物品的体积和价值。
输出格式
输出一个整数,表示最大价值。
数据范围
0<N,V≤1000
0<vi,wi≤1000
输入样例
4 5
1 2
2 4
3 4
4 5
输出样例:
8
思想
经典动态规划题
物品个数n,背包总容量v
c[]代表每个物品体积,w[]代表每个物品价值
设f[i][j],表示前i件物品放入容量为j的背包的最大价值
因此可遍历物品,并计算假设容量为j时候的最大值。
此时有两种情况:
1.容量不够(j < c[i]),则取不了当前物品,直接f[i][j]=f[i-1][j]
2.容量够,则需要判断取与不取当前物品的价值
若不取第i件,问题转化为求前i-1件物品放入容量为v的背包的问题,即f[i][j]=f[i-1][j]
若取第i件,问题转化为求前i-1件物品放入容量为j-c[i]的背包的问题,即f[i][j]=f[i-1][j-c[i]]+w[i]
所以f[i][j]即是求以上两种情况的最大值,最终状态转移方程是 f[i][v] = max(f[i-1][v], f[i-1][j-c[i]] + w[i])
最后答案就是f[n][v]
代码实现
import java.util.Scanner;
public class Main {
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();
}
System.out.println(cal(N, V, w, v));
}
/**
* 求01背包的解
* @param n 物品个数
* @param v 背包总容量
* @param w 每个物品的价值,注意下标从1开始
* @param c 每个物品的体积,注意下标从1开始
* @return
*/
private static int cal(int n, int v, int[] w, int[] c) {
/*
*/
if (n == 0 || v == 0) return 0;
if (w == null || w.length == 0) return 0;
if (c == null || c.length == 0) return 0;
int[][] f = new int[n + 1][v + 1];
for(int i=1; i<=n; i++) {
for(int j=1;j<=v;j++) {
f[i][j] = j<c[i]? f[i-1][j] : Math.max(f[i-1][j], f[i-1][j-c[i]] + w[i]);
}
}
return f[n][v];
}
}