目录
01背包板子
完全背包板子
多重背包板子
分组背包板子
2. 01背包问题 - 每种物品只有一件,体积至多j,总价值最大
1、二维dp
2、一维dp
3. 完全背包问题 - 每种物品无限件,体积至多j,总价值最大
1、二维dp
2、一维dp
4. 多重背包问题 I - 每种物品数量有限件,体积至多j,总价值最大
1、二维dp
2、一维dp
2、二进制优化版
9. 分组背包问题 - 每组物品最多选1个,体积至多j,总价值最大
1、一维dp
3382. 整数拆分 - 完全背包dp
01背包——要的是第i-1层状态,倒序遍历[m,v]
import java.util.*;class Main
{static int N=1010;static int[] f=new int[N];//f[j] N件物品,总体积不超过j的情况下,最大价值public static void main(String[] args){Scanner sc=new Scanner(System.in);int n=sc.nextInt(),m=sc.nextInt();f[0]=0;for(int i=1;i<=n;i++){int v=sc.nextInt(),w=sc.nextInt();for(int j=m;j>=v;j--)f[j]=Math.max(f[j],f[j-v]+w); //max(不选,选)}System.out.print(f[m]);}
}
完全背包——要的是第i层状态,正序遍历[v,m]
import java.util.*;class Main
{static int N=1010;static int[] f=new int[N];public static void main(String[] args){Scanner sc=new Scanner(System.in);int n=sc.nextInt(),m=sc.nextInt();for(int i=1;i<=n;i++){int v=sc.nextInt(),w=sc.nextInt();for(int j=v;j<=m;j++)f[j]=Math.max(f[j],f[j-v]+w);}System.out.print(f[m]);}
}
多重背包——要的是第i-1层,倒序遍历[m,1]注意判断j>=k*v
import java.util.*;class Main
{static int N=110;static int[] f=new int[N];public static void main(String[] args){Scanner sc=new Scanner(System.in);int n=sc.nextInt(),m=sc.nextInt();for(int i=1;i<=n;i++){int v=sc.nextInt(),w=sc.nextInt(),s=sc.nextInt();for(int j=m;j>=1;j--)for(int k=0;k<=s;k++)if(j>=k*v) f[j]=Math.max(f[j],f[j-k*v]+k*w);}System.out.print(f[m]);}
}
二进制优化版——将n个物品按二进制数装箱,每次取一个箱子的量,转化成01背包问题
import java.util.*;class Main
{static int N=250000;static int[] v=new int[N],w=new int[N];static int[] f=new int[N];public static void main(String[] args){Scanner sc=new Scanner(System.in);int n=sc.nextInt(),m=sc.nextInt();int cnt=0;for(int i=1;i<=n;i++) {int vv=sc.nextInt(),ww=sc.nextInt(),s=sc.nextInt();//对每一种物品i进行装箱for(int k=1;k<=s;k*=2){cnt++;v[cnt]=vv*k; w[cnt]=ww*k;s-=k;}if(s>0){cnt++;v[cnt]=vv*s;w[cnt]=ww*s;}}n=cnt;for(int i=1;i<=n;i++) //每次选一个箱子for(int j=m;j>=v[i];j--)f[j]=Math.max(f[j],f[j-v[i]]+w[i]);System.out.print(f[m]);}
}
分组背包——要的是第i-1层,倒序遍历[m,1] 注意判断j>=v[i][k]
import java.util.*;class Main
{static int N=110;static int[][] v=new int[N][N];static int[][] w=new int[N][N];static int[] s=new int[N];static int[] f=new int[N];public static void main(String[] args){Scanner sc=new Scanner(System.in);int n=sc.nextInt(),m=sc.nextInt();for(int i=1;i<=n;i++){s[i]=sc.nextInt();for(int j=1;j<=s[i];j++){v[i][j]=sc.nextInt();w[i][j]=sc.nextInt();}}for(int i=1;i<=n;i++)for(int j=m;j>=1;j--)for(int k=1;k<=s[i];k++)if(j>=v[i][k])f[j]=Math.max(f[j],f[j-v[i][k]]+w[i][k]);System.out.print(f[m]);}
}
2. 01背包问题 - AcWing题库
题目:
有 N 件物品和一个容量是 V 的背包。每件物品只能使用一次。
第 i 件物品的体积是 vi,价值是 wi。
求解将哪些物品装入背包,可使这些物品的总体积不超过背包容量,且总价值最大。
输出最大价值。
思路:
- 设f[i][j]:前i件物品,总体积不超过j的最大总价值
- 答案即f[n][m]
注:当前的状态依赖于之前的状态,可以理解为从初始状态开始决策,有 N 件物品,则需要 N 次决策,每一次对第 i 件物品的决策,状态f[i][j]不断由之前的状态更新而来
- 若j
- 若j≥v[i],当前背包容量够,需要决策选or不选第i个物品
- 选:f[i][j]=f[i-1][j-v[i]]+w[i](前i-1个物品且体积给第i件物体留个空+第i件物品的价值)
- 不选:f[i][j]=f[i-1][j]
- 最后f[i][j]=max(选,不选)
import java.util.*;class Main
{static int N=1010;static int[] v=new int[N],w=new int[N];static int[][] f=new int[N][N];//f[i][j] 只选前i件物品,总体积不超过j的情况下,最大价值public static void main(String[] args){Scanner sc=new Scanner(System.in);int n=sc.nextInt(),m=sc.nextInt();for(int i=1;i<=n;i++){v[i]=sc.nextInt();w[i]=sc.nextInt();}f[0][0]=0;for(int i=1;i<=n;i++)for(int j=1;j<=m;j++){if(j
思路:
可以看出f[i][~]只依赖于f[i-1][~],所以根本没必要保留之前的f[i-2][~]等状态值
- f[j]: N 件物品,背包容量j情况下的最大价值
要获得第i-1轮的状态,需要逆序 大的先更新,小的再更新
- 更新索引值较大的dp值时,需要上一层“未被更新”的索引值较小的dp值
- 在二维情况下,状态f[i][j]是由上一轮i - 1的状态得来的,而优化到一维后,如果我们还是从小到大顺序,则较小值先更新,则有可能本应该用第i-1轮的状态却用的是第i轮的状态
- 因此要保证先更新大的,后更新小的,这才是第i-1轮状态,j应该从大到小循环
import java.util.*;class Main
{static int N=1010;static int[] f=new int[N];//f[j] N件物品,总体积不超过j的情况下,最大价值public static void main(String[] args){Scanner sc=new Scanner(System.in);int n=sc.nextInt(),m=sc.nextInt();f[0]=0;for(int i=1;i<=n;i++){int v=sc.nextInt(),w=sc.nextInt();for(int j=m;j>=v;j--)f[j]=Math.max(f[j],f[j-v]+w);}System.out.print(f[m]);}
}
3. 完全背包问题 - AcWing题库
题目:
有 N 种物品和一个容量是 V 的背包,每种物品都有无限件可用。
第 i 种物品的体积是 vi,价值是 wi。
求解将哪些物品装入背包,可使这些物品的总体积不超过背包容量,且总价值最大。
输出最大价值。
思路:
- 设f[i][j]:前i件物品,总体积不超过j的最大总价值
- 答案即f[n][m]
- 选0件第i个物品:f[i][j]=f[i-1][j]
- 选1件第i个物品:f[i][j]=f[i-1][j-v]+w
- 选2件第i个物品:f[i][j]=f[i-1][j-2v]+2w
- ……
- 选k件第i个物品:f[i][j]=f[i-1][j-kv]+kw
因此 f[i][j]=max(f[i-1][j],f[i-1][j-v]+w,……,f[i-1][j-kv]+kw)
由于上述动态方程存在i,j,k三重循环,则会TLE,所以需要优化
f[i][j]=max(f[i-1][j],f[i-1][j-v]+w,f[i-1][j-2v]+2w……,f[i-1][j-kv]+kw)
将j用j-v替换得:
f[i][j-v]=max(f[i-1][j-v],f[i-1][j-2v]+w,……,f[i-1][j-(k+1)v]+kw)
则可推导:
f[i][j]=max(f[i-1][j],f[i][j-v]+w)
import java.util.*;class Main
{static int N=1010;static int[] v=new int[N],w=new int[N];static int[][] f=new int[N][N];public static void main(String[] args){Scanner sc=new Scanner(System.in);int n=sc.nextInt(),m=sc.nextInt();for(int i=1;i<=n;i++){v[i]=sc.nextInt();w[i]=sc.nextInt();}for(int i=1;i<=n;i++)for(int j=1;j<=m;j++){if(j
思路:
可以看出f[i][~]只依赖于f[i-1][~],所以根本没必要保留之前的f[i-2][~]等状态值
- f[j]: N 件物品,背包容量j情况下的最大价值
要获得第i轮的状态,需要正序 小的先更新,大的再更新
import java.util.*;class Main
{static int N=1010;static int[] f=new int[N];public static void main(String[] args){Scanner sc=new Scanner(System.in);int n=sc.nextInt(),m=sc.nextInt();for(int i=1;i<=n;i++){int v=sc.nextInt(),w=sc.nextInt();for(int j=v;j<=m;j++)f[j]=Math.max(f[j],f[j-v]+w);}System.out.print(f[m]);}
}
4. 多重背包问题 I - AcWing题库
题目:
有 N 种物品和一个容量是 V 的背包。
第 i 种物品最多有 si 件,每件体积是 vi,价值是 wi。
求解将哪些物品装入背包,可使物品体积总和不超过背包容量,且价值总和最大。
输出最大价值。
思路:
- 设f[i][j]:前i件物品,总体积不超过j的最大总价值
- 答案即f[n][m]
- 选0件第i个物品:f[i][j]=f[i-1][j]
- 选1件第i个物品:f[i][j]=f[i-1][j-v]+w
- 选2件第i个物品:f[i][j]=f[i-1][j-2v]+2w
- ……
- 选s件第i个物品:f[i][j]=f[i-1][j-sv]+sw
import java.util.*;class Main
{static int N=110;static int[] v=new int[N],w=new int[N],s=new int[N];static int[][] f=new int[N][N];public static void main(String[] args){Scanner sc=new Scanner(System.in);int n=sc.nextInt(),m=sc.nextInt();for(int i=1;i<=n;i++) {v[i]=sc.nextInt();w[i]=sc.nextInt();s[i]=sc.nextInt();}for(int i=1;i<=n;i++)for(int j=1;j<=m;j++)for(int k=0;k<=s[i];k++)if(j>=k*v[i]) f[i][j]=Math.max(f[i][j],f[i-1][j-k*v[i]]+k*w[i]);//当k=0时,f[i-1][j-v[i]*k]+w[i]*k就是f[i-1][j]System.out.print(f[n][m]);}
}
思路:
可以看出f[i][~]只依赖于f[i-1][~],所以根本没必要保留之前的f[i-2][~]等状态值
- f[j]: N 件物品,背包容量j情况下的最大价值
要获得第i-1轮的状态,需要逆序 大的先更新,小的再更新
import java.util.*;class Main
{static int N=110;static int[] f=new int[N];public static void main(String[] args){Scanner sc=new Scanner(System.in);int n=sc.nextInt(),m=sc.nextInt();for(int i=1;i<=n;i++){int v=sc.nextInt(),w=sc.nextInt(),s=sc.nextInt();for(int j=m;j>=1;j--)for(int k=0;k<=s;k++)if(j>=k*v) f[j]=Math.max(f[j],f[j-k*v]+k*w);}System.out.print(f[m]);}
}
5. 多重背包问题 II - AcWing题库
思路:
- 多重背包无法像完全背包那样用类似“错位相减”的方法优化,因此我们考虑问题转化:
- 水果店里有 40 个苹果,小明计划购买 n ( 1 ≤ n ≤ 40 ) 个苹果,试问如何让小明尽可能快速地完成购买?一个显而易见的暴力做法是,让小明一个个拿(单位是个),但效率过于低下。事实上,店员可事先准备好 6 个箱子,每个箱子中的苹果数量分别为 [1,2,4,8,16,9],再让小明按箱子拿(单位是箱子),无论小明计划购买多少个,他最多只需要拿 6 次,而在暴力做法中,小明最多需要拿 40 次。
- 在暴力求解多重背包问题时,对于每种物品i,都要从0开始枚举到s[i],效率低
- 现在对于每种物品i,将这s[i]个物体分到 ⌊ log2s[i] ⌋+1个箱子里
- 相当于转化成每次取一个箱子的量,也就是01背包问题
- 也就是将s[i]拆分成若干个二进制的数的组合
- 原本结果数为NVS=4×10^9
- 优化后为NVlog2S=24000
import java.util.*;class Main
{static int N=250000;static int[] v=new int[N],w=new int[N];static int[] f=new int[N];public static void main(String[] args){Scanner sc=new Scanner(System.in);int n=sc.nextInt(),m=sc.nextInt();int cnt=0;for(int i=1;i<=n;i++) {int vv=sc.nextInt(),ww=sc.nextInt(),s=sc.nextInt();//对每一种物品i进行装箱for(int k=1;k<=s;k*=2){cnt++;v[cnt]=vv*k; w[cnt]=ww*k;s-=k;}if(s>0){cnt++;v[cnt]=vv*s;w[cnt]=ww*s;}}n=cnt;for(int i=1;i<=n;i++) //每次选一个箱子for(int j=m;j>=v[i];j--)f[j]=Math.max(f[j],f[j-v[i]]+w[i]);System.out.print(f[m]);}
}
9. 分组背包问题 - AcWing题库
题目:
有 N 组物品和一个容量是 V 的背包。
每组物品有若干个,同一组内的物品最多只能选一个。
每件物品的体积是 vij,价值是 wij,其中 i 是组号,j 是组内编号。求解将哪些物品装入背包,可使物品总体积不超过背包容量,且总价值最大。
输出最大价值。
思路:
- 设f[j]:前N件物品,总体积不超过j的最大总价值
- 答案即f[m]
对于二维dp进行分析,三重循环会超时:
- 不选第i组的物品:f[i][j]=f[i][j]
- 选第i组第1个物品:f[i][j]=f[i-1][j-v[i][1]]+w[i][1]
- 选第i组第2个物品:f[i][j]=f[i-1][j-v[i][2]]+w[i][2]
- ……
- 选第i组第s个物品:f[i][j]=f[i-1][j-v[i][s]]+w[i][s]
要获得第i-1轮的状态,需要逆序 大的先更新,小的再更新
f[j]=max(f[j],f[j-v[i][k]]+w[i][k])
import java.util.*;class Main
{static int N=110;static int[][] v=new int[N][N];static int[][] w=new int[N][N];static int[] s=new int[N];static int[] f=new int[N];public static void main(String[] args){Scanner sc=new Scanner(System.in);int n=sc.nextInt(),m=sc.nextInt();for(int i=1;i<=n;i++){s[i]=sc.nextInt();for(int j=1;j<=s[i];j++){v[i][j]=sc.nextInt();w[i][j]=sc.nextInt();}}for(int i=1;i<=n;i++)for(int j=m;j>=1;j--)for(int k=1;k<=s[i];k++)if(j>=v[i][k])f[j]=Math.max(f[j],f[j-v[i][k]]+w[i][k]);System.out.print(f[m]);}
}
3382. 整数拆分 - AcWing题库
题目:
思路:
可以将问题转化成:
有m种物品(2的幂的种数),容量为n的背包,某种物品可以选无限个,问不超过容量n的最大方案数
也就是完全背包问题,定义f[j]为用2的幂凑出的值不超过j的最大方案数,则f[n]即是答案
import java.util.*;class Main
{static int N=1000010,mod=1000000000;static int[] f=new int[N];public static void main(String[] args){Scanner sc=new Scanner(System.in);int n=sc.nextInt();//定义f[j]用2的幂凑出的值不超过j的最大方案数f[0]=1;for(int i=1;i<=n;i*=2) //枚举2的幂数for(int j=i;j<=n;j++)f[j]=(f[j]+f[j-i])%mod;System.out.print(f[n]);}
}
上一篇:基于samba源码的ubuntu18.4搭建共享目录
下一篇: 幼儿园秋季园务工作计划