【题目描述】
设有nn种物品,每种物品有一个重量及一个价值。但每种物品的数量是无限的,同时有一个背包,最大载重量为MM,今从nn种物品中选取若干件(同一种物品可以多次选取),使其重量的和小于等于MM,而价值的和为最大。
【输入】
第一行:两个整数,MM(背包容量,M≤200M≤200)和NN(物品数量,N≤30N≤30);
第2…N+12…N+1行:每行二个整数Wi,CiWi,Ci,表示每个物品的重量和价值。
【输出】
仅一行,一个数,表示最大总价值。
【输入样例】
10 4
2 1
3 3
4 5
7 9
【输出样例】
max=12
完全背包题目连接
之前的01背包是拿与不拿的考虑(0表示不拿,1表示拿),
完全背包是一件物品可以拿多次,求最大值。
其实完全背包的状态方程和01背包是一样的,不同的是01背包参考的数据是上一轮的数据,而完全背包参考的数据是同一轮的数据。
01背包是参考旧数据,所以用逆向循环
完全背包则是用正向循环即可
想了解01背包可以参考我以前的01背包
动态规划之01背包问题
我们可以通过自己打表的方式找变化求得状态方程
(横向表示容量,j)(纵向表示第几个,i)
01背包表:
# 1 2 3 4 5 6 7 8 9 10
1 0 1 1 1 1 1 1 1 1 1
2 0 1 3 3 4 4 4 4 4 4
3 0 1 3 5 5 6 8 8 9 9
4 0 1 3 5 5 6 9 9 10 12
完全背包表:
# 1 2 3 4 5 6 7 8 9 10
1 0 1 1 2 2 3 3 4 4 5
2 0 1 3 3 4 6 6 7 9 9
3 0 1 3 5 5 6 8 10 10 11
4 0 1 3 5 5 6 9 10 10 12
dp[i][j]----i表示物品个数,j表示容量,dp[i][j]的值表示在此状态时的最大价值
以下是两种情况的状态转移方程:
01背包:
dp[i][j]=Math.max(dp[i-1][j],dp[i-1][j-w[i]]+v[i])//上一条
dp[j]=Math.max(dp[j],dp[j-w[i]+v[i]])//逆向,用到的是旧数据
完全背包:
dp[i][j]=Math.max(dp[i-1][j],dp[i][j-w[i]]+v[i])//本条
dp[j]=Math.max(dp[j],dp[j-w[i]+v[j]])//顺向,用到是新数据
①
根据题意我们可以这样写,但是这样写用了3个循环,太耗时了,我们可以想办法优化
for (int i = 1; i <= n; i++)
{
for (int j =m; j >=1; j--)
{
for(int k=0;k<=j/w[i];k++)
dp[j]=Math.max(dp[j],dp[jk*w[i]]+v[i]);
}
}
②
for (int i = 1; i <= n; i++)
{
for (int j = 1; j <= m; j++) //正向循环
{
if(j>=w[i])
dp[j]=Math.max(dp[j],dp[j-w[i]]+v[i]);
}
}
③再次优化,少个判断
for (int i = 1; i <= n; i++)
{
for (int j = w[i]; j <= m; j++)
{
dp[j]=Math.max(dp[j],dp[j-w[i]]+v[i]);
}
}
完整代码如下:
import java.util.*;
import java.lang.*;
public class Main {
static int[]dp=new int[205];
static int[] w = new int[35];
static int[] v = new int[35];
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
int m, n;
m = in.nextInt();
n = in.nextInt();
for (int i = 1; i <= n; i++) {
w[i] = in.nextInt();
v[i] = in.nextInt();
}
for (int i = 1; i <= n; i++) {
for (int j = w[i]; j <= m; j++) {
dp[j]=Math.max(dp[j],dp[j-w[i]]+v[i]);
}
}
System.out.println(dp[m]);
in.close();
}
}