AcWing-3.完全背包问题
题目描述
有 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
输出样例:
10
思路(朴素版)
状 态 表 示 : f ( i , j ) , 表 示 从 1 到 i 中 选 容 量 不 超 过 j 的 所 有 价 值 集 合 状态表示:f(i,j),表示从1到i中选容量不超过j的所有价值集合 状态表示:f(i,j),表示从1到i中选容量不超过j的所有价值集合
属 性 : M a x 属性:Max 属性:Max
核 心 : 最 后 一 个 数 i 不 取 或 者 取 k 个 ( k 最 大 值 为 i 的 最 大 可 取 数 , 即 取 了 k 个 i 后 总 容 量 没 有 超 过 j ) 核心:最后一个数i不取或者取k个(k最大值为i的最大可取数,即取了k个i后总容量没有超过j) 核心:最后一个数i不取或者取k个(k最大值为i的最大可取数,即取了k个i后总容量没有超过j)
状 态 划 分 : f ( i − 1 , j ) 取 0 个 i , 1 个 i … … , k 个 i 变 为 f ( i , j ) 状态划分:f(i-1,j)取0个i,1个i……,k个i变为f(i,j) 状态划分:f(i−1,j)取0个i,1个i……,k个i变为f(i,j)
由状态划分可得到状态转移方程:
f ( i , j ) = M a x ( f ( i − 1 , j ) , f ( i − 1 , j − v i ) + w i , f ( i − 1 , j − 2 ∗ v i ) + 2 ∗ w i , … … , f ( i − 1 , j − k ∗ v i ) + k ∗ w i ) f(i,j) = Max(f(i-1,j),f(i-1,j-vi)+wi,f(i-1,j-2*vi)+2*wi,……,f(i-1,j-k*vi)+k*wi) f(i,j)=Max(f(i−1,j),f(i−1,j−vi)+wi,f(i−1,j−2∗vi)+2∗wi,……,f(i−1,j−k∗vi)+k∗wi)
核心代码
for(int i=1;i<=n;i++) {
for(int j=1;j<=v;j++) {
dp[i][j] = dp[i-1][j];
if(a[i].v<=j) {
for(int k=1;k<=j/a[i].v;k++) {
dp[i][j] = Math.max(dp[i][j],dp[i-1][j-k*a[i].v]+k*a[i].w);
}
}
}
}
完整代码
package acWing003;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.StreamTokenizer;
public class Main {
static int N = 1010;
static int n,v;
static Pair a[] = new Pair[N];
static int dp[][] = new int[N][N];
public static void main(String[] args) throws Exception{
Read r = new Read();
n = r.nextInt();v = r.nextInt();
for(int i=1;i<=n;i++) {
a[i] = new Pair(r.nextInt(),r.nextInt());
}
for(int i=1;i<=n;i++) {
for(int j=1;j<=v;j++) {
dp[i][j] = dp[i-1][j];
if(a[i].v<=j) {
for(int k=1;k<=j/a[i].v;k++) {
dp[i][j] = Math.max(dp[i][j],dp[i-1][j-k*a[i].v]+k*a[i].w);
}
}
}
}
System.out.println(dp[n][v]);
}
}
class Pair{
int v,w;
public Pair(int a,int b){
v = a;
w = b;
}
}
class Read{
StreamTokenizer st = new StreamTokenizer(new BufferedReader(new InputStreamReader(System.in)));
public int nextInt() throws Exception{
st.nextToken();
return (int)st.nval;
}
}
优化(状态优化)
由状态划分可得到状态转移方程:
f ( i , j ) = M a x ( f ( i − 1 , j ) , f ( i − 1 , j − v i ) + w i , f ( i − 1 , j − 2 ∗ v i ) + 2 ∗ w i , … … , f ( i − 1 , j − k ∗ v i ) + k ∗ w i ) f(i,j) = Max(f(i-1,j),f(i-1,j-vi)+wi,f(i-1,j-2*vi)+2*wi,……,f(i-1,j-k*vi)+k*wi) f(i,j)=Max(f(i−1,j),f(i−1,j−vi)+wi,f(i−1,j−2∗vi)+2∗wi,……,f(i−1,j−k∗vi)+k∗wi)
f ( i , j − v i ) = M a x ( f ( i − 1 , j − v i ) , f ( i − 1 , j − 2 ∗ v i ) + w i , … … , f ( i − 1 , j − k ∗ v i ) + ( k − 1 ) ∗ w i ) f(i,j-vi) = ~~~~~~~~~~~~~~Max(f(i-1,j-vi),~~~~~~~~~f(i-1,j-2*vi)+wi,……,f(i-1,j-k*vi)+(k-1)*wi) f(i,j−vi)= Max(f(i−1,j−vi), f(i−1,j−2∗vi)+wi,……,f(i−1,j−k∗vi)+(k−1)∗wi)
合并得最终的状态转移方程:
f ( i , j ) = M a x ( f ( i − 1 , j ) , f ( i , j − v i ) + w i ) f(i,j) = Max(f(i-1,j),f(i,j-vi)+wi) f(i,j)=Max(f(i−1,j),f(i,j−vi)+wi)
核心代码
for(int i=1;i<=n;i++) {
for(int j=1;j<=v;j++) {
dp[i][j] = dp[i-1][j];
if(a[i].v<=j) {
dp[i][j] = Math.max(dp[i][j],dp[i][j-a[i].v]+a[i].w);
}
}
}
完整代码
package acWing003;
// 读入和Pair类都在朴素版
public class Main2 {
static int N = 1010;
static int n,v;
static Pair a[] = new Pair[N];
static int dp[][] = new int[N][N];
public static void main(String[] args) throws Exception{
Read r = new Read();
n = r.nextInt();v = r.nextInt();
for(int i=1;i<=n;i++) {
a[i] = new Pair(r.nextInt(),r.nextInt());
}
for(int i=1;i<=n;i++) {
for(int j=1;j<=v;j++) {
dp[i][j] = dp[i-1][j];
if(a[i].v<=j) {
dp[i][j] = Math.max(dp[i][j],dp[i][j-a[i].v]+a[i].w);
}
}
}
System.out.println(dp[n][v]);
}
}
优化(利用滚动数组降维)
合并得最终的状态转移方程为:
f ( i , j ) = M a x ( f ( i − 1 , j ) , f ( i , j − v i ) + w i ) f(i,j) = Max(f(i-1,j) , f(i,j-vi)+wi) f(i,j)=Max(f(i−1,j),f(i,j−vi)+wi)
用状态方程可知,需要利用的仅有当前行和上一行所以可以利用滚动数组的概念将二维降至一维,即:
f ( j ) = M a x ( f ( j ) , f ( j − v i ) + w i ) f(j) = Max(f(j),f(j-vi)+wi) f(j)=Max(f(j),f(j−vi)+wi)
核心代码
for(int i=1;i<=n;i++) {
for(int j=a[i].v;j<=v;j++) {
dp[j] = Math.max(dp[j],dp[j-a[i].v]+a[i].w);
}
}
完整代码
package acWing003;
// 读入和Pair类都在朴素版
public class Main3 {
static int N = 1010;
static int n,v;
static Pair a[] = new Pair[N];
static int dp[] = new int[N];
public static void main(String[] args) throws Exception{
Read r = new Read();
n = r.nextInt();v = r.nextInt();
for(int i=1;i<=n;i++) {
a[i] = new Pair(r.nextInt(),r.nextInt());
}
for(int i=1;i<=n;i++) {
for(int j=a[i].v;j<=v;j++) {
dp[j] = Math.max(dp[j],dp[j-a[i].v]+a[i].w);
}
}
System.out.println(dp[v]);
}
}