完全背包问题
Description:
有N种物品和一个最大承重为M的背包,每种物品都有无限件可用。
求解将哪些物品装入背包可使这些物品的费用总和不超过背包最大承重,且价值总和最大。
每种物品具有 Vi价值,Wi的重量,数量无限。
Input:
第一行输入两个整数,N,M。
余下N行每行输入两个整数,Wi和Vi。
Output:
输出最大价值。
样例:
Input:
3 7
3 4
4 5
2 3
Output:
7
首先,众所周知,这是一个动态规划的基本问题。
然后,一个比较简单的状态转移方程。
dp[i+1][j] = max{dp[i][j-k*w[i]]+k*v[i]|0<=k && j >= k*w[i]}
这个方程对应 solve_1();的方法。
dp[i][j] 代表从前i种物品中挑选总重量不超过i时总价值的最大值。
举个例子,拿上面的当数据,求dp[3][4].
就需要比较dp[2][4]和dp[2][4-1*w[3]]+1*v[3]的大小。dp[3][4]就是大的那一个了。
其中dp[2][4]就是dp[2][4-0*w[3]]+0*v[3].
然后这个方程呢,有一些比较坑爹的地方,他做了很多重复计算。
假设我们已知dp[i][j-1*w[i]]的时候,那我们就已经知道了:从前i种物品中挑选总重量不超过i时总价值的最大值。
那么这就意味着,下面的方程永远成立
dp[i][j-1*w[i]] >= max{dp[i][j-k*w[i]]+k*v[i]|k>=2&&k*w[i]<=j}
然后我们就不需要考虑k>1的所有情况了.
所以就有了新的状态转移方程: dp[i+1][j]=max(dp[i][j],dp[i+1][j-w[i]]+v[i])。这个也就是solve_2();
(要注意,dp[i][j-2*w[i]]+2*v[i]这个东西,是在dp[i+1][j-w[i]]+v[i])中被计算的。)
其中关于空间复杂度的优化。
这个嘛,solve_3有轮子阿!
其实就是改一改solve_2的...
具体观察方程,就会发现需要用到的只是i和i+1那一层,那就可以以奇数为1,偶数为0来实现辣。就这么简单。
import java.util.Scanner;
public class Beibao {
static int[][] dp;
static int n,W;
static int[] w,v;
public static void main(String[] args) {
Scanner input = new Scanner(System.in);
n = input.nextInt();
W = input.nextInt();
w = new int[n+1];
v = new int[n+1];
for(int i = 0 ; i<n ; i++){
w[i] = input.nextInt();
v[i] = input.nextInt();
}
solve_1();
solve_2();
}
static void solve_1(){
dp = new int[n+1][W+1];
for(int i = 0 ; i<n ; i++ ){
for(int j = 0 ; j<=W ; j++){
for(int k = 0 ; k*w[i] <= j ; k++){
dp[i+1][j] = Math.max( dp[i+1][j] , dp[i][j-k*w[i]] + k*v[i]);
}
}
}
System.out.println(dp[n][W]);
}
static void solve_2(){
dp = new int[n+1][W+1];
for(int i = 0 ; i<n ; i++){
for(int j = 0 ; j <= W ; j++){
if( j < w[i] ){
dp[i+1][j] = dp[i][j];
}else{
dp[i+1][j] = Math.max(dp[i][j], dp[i+1][j-w[i]]+v[i]);
}
}
}
System.out.println(dp[n][W]);
}
static void solve_3(){
dp = new int[2][W+1];
for(int i = 0 ; i<n ; i++){
for(int j = 0 ; j <= W ; j++){
if( j < w[i] ){
dp[(i+1)%2][j] = dp[i%2][j];
}else{
dp[(i+1)%2][j] = Math.max(dp[i%2][j], dp[(i+1)%2][j-w[i]]+v[i]);
}
}
}
System.out.println(dp[1][W]);
}
}