diff --git "a/ACWing/src/dp/\346\240\221\345\275\242dp/\346\234\211\344\276\235\350\265\226\347\232\204\350\203\214\345\214\205\351\227\256\351\242\230.java" "b/ACWing/src/dp/\346\240\221\345\275\242dp/\346\234\211\344\276\235\350\265\226\347\232\204\350\203\214\345\214\205\351\227\256\351\242\230.java" index 6549d311f540a2d95111f776dfad959ef58db695..8b98f58f37d05c68673723635d83740086e115f4 100644 --- "a/ACWing/src/dp/\346\240\221\345\275\242dp/\346\234\211\344\276\235\350\265\226\347\232\204\350\203\214\345\214\205\351\227\256\351\242\230.java" +++ "b/ACWing/src/dp/\346\240\221\345\275\242dp/\346\234\211\344\276\235\350\265\226\347\232\204\350\203\214\345\214\205\351\227\256\351\242\230.java" @@ -3,20 +3,7 @@ package dp.树形dp; import java.util.Scanner; /** - * 有n个物品和容量为V的背包 - * 物品之间有依赖关系,关系组成一颗树的形状, - * 如果要选一个物品,则必选他的父节点 - * 求最大价值 - * dfs+dp dp.树形dp - * 状态定义:集合f[u,j]:所有从以u为根的子树中选,且总体积不超过j的选法 - * 属性:max最大价值 - * 集合划分:子树1,子树3,子树3 - * 再根据体积划分子树:体积是0-m 有m+1总可能 - * 用一个数字表示一类方案 - * 把每一颗子树看做物品组,分组背包问题 - * 链式前向星建图 - * 有 N 个物品和一个容量是 V - * 的背包。 + * 有 N 个物品和一个容量是 V的背包。 * 物品之间具有依赖关系,且依赖关系组成一棵树的形状。如果选择一个物品,则必须选择它的父节点。 * 如下图所示: * 如果选择物品5,则必须选择物品1和2。这是因为2是5的父节点,1是2的父节点。 @@ -49,6 +36,20 @@ import java.util.Scanner; * 3 6 2 * 输出样例: * 11 + *

+ * 有n个物品和容量为V的背包 + * 物品之间有依赖关系,关系组成一颗树的形状, + * 如果要选一个物品,则必选他的父节点 + * 求最大价值 + * dfs+dp dp.树形dp + * 状态定义:集合f[u,j]:所有从以u为根的子树中选,且总体积不超过j的选法 + * 属性:max最大价值 + * 集合划分:子树1,子树3,子树3 + * 划分依据:体积为0~m的子树,把每一棵子树看做一个物品做 + * 再根据体积划分子树:体积是0-m 有m+1总可能 + * 用一个数字表示一类方案 + * 把每一颗子树看做物品组,分组背包问题 + * 链式前向星建树 */ public class 有依赖的背包问题 { @@ -62,7 +63,7 @@ public class 有依赖的背包问题 { w[i] = sc.nextInt(); p = sc.nextInt(); if (p == -1) root = i; - else add(p, i); + else add(p, i);//p向i连一条边,父节点向子节点连边 } dfs(root); System.out.println(f[root][m]); @@ -74,7 +75,7 @@ public class 有依赖的背包问题 { dfs(e[i]); //以u为根的子树,每一棵子树都是一个分组背包问题 //抽象成2层 - for (int j = m - v[u]; j >= 0; j--) {//枚举体积 + for (int j = m - v[u]; j >= 0; j--) {//枚举体积,因为根节点必须要选,预留m-v[u for (int k = 0; k <= j; k++) {//枚举决策,分为以0~m的一些决策 f[u][j] = Math.max(f[u][j], f[u][j - k] + f[son][k]); //以u为根节点,在剩余体积为0~m-v[u]的情况下,选子树 @@ -95,11 +96,11 @@ public class 有依赖的背包问题 { static int N = 110; static int[] head = new int[N], e = new int[N], ne = new int[N]; - static int n, m, cnt = 1; + static int n, m, idx = 1; static void add(int a, int b) { - e[cnt] = b; - ne[cnt] = head[a]; - head[a] = cnt++; + e[idx] = b; + ne[idx] = head[a]; + head[a] = idx++; } } diff --git "a/ACWing/src/dp/\347\212\266\346\200\201\345\216\213\347\274\251dp/\345\223\210\345\257\206\345\260\224\351\241\277\345\233\236\350\267\257.java" "b/ACWing/src/dp/\347\212\266\346\200\201\345\216\213\347\274\251dp/\345\223\210\345\257\206\345\260\224\351\241\277\345\233\236\350\267\257.java" index ff6b2ff0fb8078e91cfc1d6eb63221e375779b61..0585f119525e3aad92e00993b9d67e7540721bb1 100644 --- "a/ACWing/src/dp/\347\212\266\346\200\201\345\216\213\347\274\251dp/\345\223\210\345\257\206\345\260\224\351\241\277\345\233\236\350\267\257.java" +++ "b/ACWing/src/dp/\347\212\266\346\200\201\345\216\213\347\274\251dp/\345\223\210\345\257\206\345\260\224\351\241\277\345\233\236\350\267\257.java" @@ -29,7 +29,7 @@ import static java.lang.Math.min; * 18 * 状压dp * 该图是完全图 - * 2的整数次幂-1的二进制位全是1111111 + * 2的整数次幂-1的二进制位全是1 * f[i,j]状态表示为从0走到顶点j状态为i的所有走法 */ public class 哈密尔顿回路 { diff --git "a/ACWing/src/dp/\347\212\266\346\200\201\345\216\213\347\274\251dp/\351\252\221\345\243\253.java" "b/ACWing/src/dp/\347\212\266\346\200\201\345\216\213\347\274\251dp/\351\252\221\345\243\253.java" index ce5e2678234a6deec8d67a439123da432b9381fe..0287d4caab0a500eae8ff150290d83be0545ea8c 100644 --- "a/ACWing/src/dp/\347\212\266\346\200\201\345\216\213\347\274\251dp/\351\252\221\345\243\253.java" +++ "b/ACWing/src/dp/\347\212\266\346\200\201\345\216\213\347\274\251dp/\351\252\221\345\243\253.java" @@ -39,10 +39,9 @@ import java.util.Scanner; * 另外,a中的合法状态可以转移到哪些合法的状态,也可以预处理出来存进向量b中。 * 本题方案数可能很大,需要用long long存储。 *

- * f[i,j,k] + * f[i,j,k] k为二进制数,表示在哪里放了国王 * 状态定义:所有只从前i行摆放,已经摆了j个国王,并且第i行的摆放状态是k的所有方案的集合 * 属性count - * */ public class 骑士 { public static void main(String[] args) { @@ -52,7 +51,7 @@ public class 骑士 { for (int i = 0; i < 1 << n; i++) { if (check(i)) { a.add(i);//去除相邻的放置方法 - cnt.add(count(i)); + cnt.add(count(i));//预处理i作为 } } int s = 0, t = 0, u = 0; diff --git "a/ACWing/src/dp/\347\212\266\346\200\201\346\234\272\346\250\241\345\236\213/\345\244\247\347\233\227\351\230\277\347\246\217.java" "b/ACWing/src/dp/\347\212\266\346\200\201\346\234\272\346\250\241\345\236\213/\345\244\247\347\233\227\351\230\277\347\246\217.java" index 195c25abf9cf095c9e29c179fe57c1a8e22beda3..51f62724532bbecfdb1f6ff15b07e58a66722c44 100644 --- "a/ACWing/src/dp/\347\212\266\346\200\201\346\234\272\346\250\241\345\236\213/\345\244\247\347\233\227\351\230\277\347\246\217.java" +++ "b/ACWing/src/dp/\347\212\266\346\200\201\346\234\272\346\250\241\345\236\213/\345\244\247\347\233\227\351\230\277\347\246\217.java" @@ -88,7 +88,12 @@ public class 大盗阿福 { * 状态转移,看成图论问题 * f[i,0] f[i,1] 表示:所有走了i步,且当前位于状态j的所有走法 * 属性:max - * 状态计算:f[i,0] 0->0 上一个不偷,这个一个也不偷, f[i-1,0] + * 状态0代表没偷,状态1代表偷了 + * 0->0 + * 0->1 + * 1->0 + * 只有这三种转移方式 + * 状态计算:f[i,0] 0->0 上一个不偷,这个一个也不偷, 对应就是f[i-1,0] * 1->0 上一个偷了,这个不偷 f[i-1,1] * f[i,1] 状态计算,只能0->1 上个不偷这个偷,f[i-1,0]+w[i] */ diff --git "a/ACWing/src/dp/\347\212\266\346\200\201\346\234\272\346\250\241\345\236\213/\350\202\241\347\245\250\344\271\260\345\215\2264.java" "b/ACWing/src/dp/\347\212\266\346\200\201\346\234\272\346\250\241\345\236\213/\350\202\241\347\245\250\344\271\260\345\215\2264.java" index 485f2aec203da81634e783736a0700abdaef3c91..d4a89311d5d35b2a5c12eec439f5ff9998a06b22 100644 --- "a/ACWing/src/dp/\347\212\266\346\200\201\346\234\272\346\250\241\345\236\213/\350\202\241\347\245\250\344\271\260\345\215\2264.java" +++ "b/ACWing/src/dp/\347\212\266\346\200\201\346\234\272\346\250\241\345\236\213/\350\202\241\347\245\250\344\271\260\345\215\2264.java" @@ -47,6 +47,8 @@ import java.util.Scanner; * f[i][j][1] = f[i-1][j][1],故f[i][j][1] = max(f[i-1][j][1],f[i-1][j-1][0]-w[i])。 * 从而状态转移方程就求出来了,下面考虑边界状态,f[i][0][0]表示第i天都未进行交易, * 收益是0,除此之外,f[i][[j][0]与f[i][0][1]的初始状态都应该是不合法的,设置为-INF。 + *

+ * 引入一层状态用来存交易次数 */ public class 股票买卖4 { public static void main(String[] args) { @@ -68,7 +70,7 @@ public class 股票买卖4 { for (int i = 0; i <= n; i++) { for (int j = 0; j <= k; j++) { for (int l = 0; l < 2; l++) { - f[i][j][l] = Integer.MIN_VALUE / 2; + f[i][j][l] = -0x3f3f3f3f; } } }//初始化负无穷 @@ -79,10 +81,12 @@ public class 股票买卖4 { f[i][j][0] = Math.max(f[i - 1][j][0], f[i - 1][j][1] + a[i]); //卖出不消耗次数 f[i][j][1] = Math.max(f[i - 1][j][1], f[i - 1][j - 1][0] - a[i]); - //昨天买的,今天不卖, 昨天交易次数为j-1没买,今天买上 + //昨天买的,今天不动, 昨天交易次数为j-1没买,今天买上 //买入消耗次数 } } + + //查看进行i次交易能得到利润最大值 int res = 0; for (int i = 0; i <= k; i++) { res = Math.max(res, f[n][i][0]); diff --git "a/ACWing/src/dp/\350\203\214\345\214\205\346\250\241\345\236\213/\344\271\260\344\271\246.java" "b/ACWing/src/dp/\350\203\214\345\214\205\346\250\241\345\236\213/\344\271\260\344\271\246.java" index 2573cb9dd227f2e2957aa14421cd188162788c2a..008cbba56515724ec9529a084f3cd450521ea345 100644 --- "a/ACWing/src/dp/\350\203\214\345\214\205\346\250\241\345\236\213/\344\271\260\344\271\246.java" +++ "b/ACWing/src/dp/\350\203\214\345\214\205\346\250\241\345\236\213/\344\271\260\344\271\246.java" @@ -46,7 +46,7 @@ public class 买书 { } static int n = 400; - static int v[] = {0, 10, 20, 50, 100}; + static int[] v = {0, 10, 20, 50, 100}; static int[] dp = new int[1010]; static int[][] f = new int[5][1010]; } diff --git "a/ACWing/src/dp/\350\203\214\345\214\205\346\250\241\345\236\213/\345\210\206\347\273\204\350\203\214\345\214\205\351\227\256\351\242\230.java" "b/ACWing/src/dp/\350\203\214\345\214\205\346\250\241\345\236\213/\345\210\206\347\273\204\350\203\214\345\214\205\351\227\256\351\242\230.java" index 58078b6e58646c1b0cced085807a0d2d8d844d86..65a3f3d13439f0410e93382ead591b1fb9a93306 100644 --- "a/ACWing/src/dp/\350\203\214\345\214\205\346\250\241\345\236\213/\345\210\206\347\273\204\350\203\214\345\214\205\351\227\256\351\242\230.java" +++ "b/ACWing/src/dp/\350\203\214\345\214\205\346\250\241\345\236\213/\345\210\206\347\273\204\350\203\214\345\214\205\351\227\256\351\242\230.java" @@ -5,8 +5,9 @@ import java.util.Scanner; /** * 有N组物品,每个组只能选1个, * 求最大价值 - * 转换成01背包问题, + * 类似01背包问题, * 状态定义为f[i,j]:前i组物品可选,体积不超过j的所有选法最大值 + * 属性max * 划分依据,选第i组物品的第几个,还是不选第i组物品 * 状态划分:选第i组物品的第1个,选第i组物品的第2个,选第i组物品的第s[i]个,不选第i组物品 * 状态计算:显然不选第i组物品就是f[i-1,j] diff --git "a/ACWing/src/dp/\350\203\214\345\214\205\346\250\241\345\236\213/\345\244\232\351\207\215\350\203\214\345\214\2051.java" "b/ACWing/src/dp/\350\203\214\345\214\205\346\250\241\345\236\213/\345\244\232\351\207\215\350\203\214\345\214\2051.java" index f81246e43829932b8b4f225ce272e686981aca8d..a0e12e83995303757227353c38f318940b517094 100644 --- "a/ACWing/src/dp/\350\203\214\345\214\205\346\250\241\345\236\213/\345\244\232\351\207\215\350\203\214\345\214\2051.java" +++ "b/ACWing/src/dp/\350\203\214\345\214\205\346\250\241\345\236\213/\345\244\232\351\207\215\350\203\214\345\214\2051.java" @@ -5,8 +5,10 @@ import java.util.Scanner; /** * 完全背包变形多重背包:每样物品最多选s[i]个 * 同理状态定义:f[i,j]代表体积为j前i个物品可选的最大价值 + * 划分依据:选第i个物品多少个 * 状态划分:选0个第i个物品,选1个第i个物品,选2个第i个物品,选3个第i个物品....选s[i]个第i个物品 * 状态计算,不失一般性,选k个:f[i-1,j-k*v[i]]+w[i] 只有在j>=k*v[i]并且k<=s[i]的时候才合法 + * O(n^3) */ public class 多重背包1 { public static void main(String[] args) { diff --git "a/ACWing/src/dp/\350\203\214\345\214\205\346\250\241\345\236\213/\345\244\232\351\207\215\350\203\214\345\214\205\344\272\214\350\277\233\345\210\266\344\274\230\345\214\226.java" "b/ACWing/src/dp/\350\203\214\345\214\205\346\250\241\345\236\213/\345\244\232\351\207\215\350\203\214\345\214\205\344\272\214\350\277\233\345\210\266\344\274\230\345\214\226.java" index b11d43aa340049d6607da72d0471342060635a72..73716c72a144d7b8dfe7623e441d99e1e1de2161 100644 --- "a/ACWing/src/dp/\350\203\214\345\214\205\346\250\241\345\236\213/\345\244\232\351\207\215\350\203\214\345\214\205\344\272\214\350\277\233\345\210\266\344\274\230\345\214\226.java" +++ "b/ACWing/src/dp/\350\203\214\345\214\205\346\250\241\345\236\213/\345\244\232\351\207\215\350\203\214\345\214\205\344\272\214\350\277\233\345\210\266\344\274\230\345\214\226.java" @@ -17,6 +17,32 @@ import java.util.Scanner; */ public class 多重背包二进制优化 { public static void main(String[] args) { + Scanner sc = new Scanner(System.in); + N = sc.nextInt(); + V = sc.nextInt(); + int a, b, c; + 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 k = 1; k <= s[i]; k *= 2) {//直接变成01背包 + for (int j = V; j >= v[i]; j--) { + dp[j] = Math.max(dp[j - v[i]] + w[i], dp[j]); + } + s[i] -= k; + } + if (s[i] > 0) { + for (int j = V; j >= v[i]; j--) { + dp[j] = Math.max(dp[j - v[i]] + w[i], dp[j]); + } + } + } + System.out.println(dp[V]); + } + + static void yuchuli() { Scanner sc = new Scanner(System.in); N = sc.nextInt(); V = sc.nextInt(); @@ -38,7 +64,7 @@ public class 多重背包二进制优化 { v[cnt] = a * c; w[cnt] = b * c; } - } + }//二进制预处理 N = cnt;//二进制优化后,一共有n个物品,01背包 for (int i = 1; i <= N; i++) { for (int j = V; j >= v[i]; j--) { @@ -48,8 +74,9 @@ public class 多重背包二进制优化 { System.out.println(dp[V]); } - static int cnt=0; - static int dp[] = new int[2010]; + static int cnt = 0; + static int[] dp = new int[2010]; static int[] v = new int[1000 * 13], w = new int[1000 * 13]; + static int[] s = new int[13000]; static int N, V; } diff --git "a/ACWing/src/dp/\350\203\214\345\214\205\346\250\241\345\236\213/\346\225\260\345\255\227\347\273\204\345\220\210.java" "b/ACWing/src/dp/\350\203\214\345\214\205\346\250\241\345\236\213/\346\225\260\345\255\227\347\273\204\345\220\210.java" index 5c8ab47747cf999e2a8f959a8c895a1a141f532a..a567d4514bd53dcfaf907394d73194324b917f1d 100644 --- "a/ACWing/src/dp/\350\203\214\345\214\205\346\250\241\345\236\213/\346\225\260\345\255\227\347\273\204\345\220\210.java" +++ "b/ACWing/src/dp/\350\203\214\345\214\205\346\250\241\345\236\213/\346\225\260\345\255\227\347\273\204\345\220\210.java" @@ -23,11 +23,15 @@ import java.util.Scanner; * M看做背包容量,每一个数看作物品,把Ai看做体积 * 求出总体积为M的所有方案数量 * 状态定义:f[i,j],集合:所有只从前i个物品选,且恰好总体积是j的方案总和 - * 属性:count + * 属性:count,方案数 * 状态划分:包含第i个,不包含第i个 + * 划分依据:包不包含当前物品i * 状态计算:不包含第i个->f[i-1,j] * 包含第i个->f[i-1,j-v[i]] * 状态合并: + * 显然:从前i个物品选且总体积恰好为j方案应当等于如下 + * 前i-1种物品且总体积为j的方案加上前i-1种物品可选总体积恰好为j-v[i] + * 因为前i-1种物品可选总体积恰好为j-v[i]再加上第重量为v[i]的第i种物品恰好是一种合法方案 * 所以f[i,j]=f[i-1,j]+f[i-1,j-v[i]] */ public class 数字组合 { @@ -36,15 +40,15 @@ public class 数字组合 { n = sc.nextInt(); m = sc.nextInt(); for (int i = 1; i <= n; i++) { - A[i] = sc.nextInt(); + v[i] = sc.nextInt(); } // one(); f[0][0] = 1; for (int i = 1; i <= n; i++) { for (int j = 0; j <= m; j++) { f[i][j] = f[i - 1][j]; - if (j >= A[i]) - f[i][j] += f[i - 1][j - A[i]]; + if (j >= v[i]) + f[i][j] += f[i - 1][j - v[i]]; } } System.out.println(f[n][m]); @@ -54,14 +58,14 @@ public class 数字组合 { static void one() { dp[0] = 1; for (int i = 1; i <= n; i++) { - for (int j = m; j >= A[i]; j--) { - dp[j] += dp[j - A[i]]; + for (int j = m; j >= v[i]; j--) { + dp[j] += dp[j - v[i]]; } } System.out.println(dp[m]); } - static int[] A = new int[110]; + static int[] v = new int[110]; static int[][] f = new int[110][10010]; static int[] dp = new int[10010]; static int n, m; diff --git "a/ACWing/src/dp/\350\203\214\345\214\205\346\250\241\345\236\213/\346\231\256\351\200\232\344\272\214\347\273\264\350\264\271\347\224\250\350\203\214\345\214\205.java" "b/ACWing/src/dp/\350\203\214\345\214\205\346\250\241\345\236\213/\346\231\256\351\200\232\344\272\214\347\273\264\350\264\271\347\224\250\350\203\214\345\214\205.java" index edbf04849e1900f777d5bd0ce7c1685abeaa4c7f..64ee398673f7a057d6f5bf22e7175caa3bca6888 100644 --- "a/ACWing/src/dp/\350\203\214\345\214\205\346\250\241\345\236\213/\346\231\256\351\200\232\344\272\214\347\273\264\350\264\271\347\224\250\350\203\214\345\214\205.java" +++ "b/ACWing/src/dp/\350\203\214\345\214\205\346\250\241\345\236\213/\346\231\256\351\200\232\344\272\214\347\273\264\350\264\271\347\224\250\350\203\214\345\214\205.java" @@ -30,7 +30,7 @@ import java.util.Scanner; * 输出样例: * 8 * 分析:背包有重量和体积限制,每个物品可选可不选,求最大价值 - * 状态表示:f[i,j,k] 所有只从前i种物品选,总体积超过j,总重量不超过k的选法 + * 状态表示:f[i,j,k] 所有只从前i种物品选,总体积不超过j,总重量不超过k的选法 * 属性: max 最大价值 * 状态划分:所有包含第i个物品的选法,所有不包含物品i的选法 * 不选第i个物品:f[i-1,j,k] diff --git "a/ACWing/src/dp/\350\203\214\345\214\205\346\250\241\345\236\213/\346\234\272\345\231\250\345\210\206\351\205\215.java" "b/ACWing/src/dp/\350\203\214\345\214\205\346\250\241\345\236\213/\346\234\272\345\231\250\345\210\206\351\205\215.java" index f49803c00f06473f9db53a25cbcaef0446d9a965..b62357988e7faca3897eacf07ab95f518972d3e3 100644 --- "a/ACWing/src/dp/\350\203\214\345\214\205\346\250\241\345\236\213/\346\234\272\345\231\250\345\210\206\351\205\215.java" +++ "b/ACWing/src/dp/\350\203\214\345\214\205\346\250\241\345\236\213/\346\234\272\345\231\250\345\210\206\351\205\215.java" @@ -29,12 +29,14 @@ import java.util.Scanner; * 2 1 * 3 1 * 分析: + * 抽象为分组背包问题 * 本题相当于AcWing 9 分组背包问题问题。每组物品要么一个不选, * 要么同一组最多选择其中的一个,本题每个公司可获得的设备数量也是只能选择一个, * 故状态表示f[i][j]表示在前i家公司中选择分配不超过j台设备的最大盈利。 * 状态转移方程为f[i][j] = max(f[i-1][j-k]+w[i][k]),其中j >= k。 * 另外题目要求输出任意一组合法方案,所以用个数组存储方案即可,具体第i组物品选与不选, - * 如果选,选几个,只需要找出其中的一个k,使得f[i][j] == f[i-1][j-k]+w[i][k]即可使得第i个物品选k个是合法的方案。 + * 如果选,选几个,只需要找出其中的一个k, + * 使得f[i][j] == f[i-1][j-k]+w[i][k]即可使得第i个物品选k个是合法的方案。 */ public class 机器分配 { public static void main(String[] args) { @@ -59,8 +61,9 @@ public class 机器分配 { int j = m; for (int i = n; i != 0; i--) {//倒序往前找决策 for (int k = 0; k <= j; k++) { + //k从0开始使得存在不选第i组物品的方案 if (f[i][j] == f[i - 1][j - k] + w[i][k]) { - way[i] = k; + way[i] = k;//这个是倒序存的,所以最终可以正序输出 j -= k; break; } diff --git "a/ACWing/src/dp/\350\203\214\345\214\205\346\250\241\345\236\213/\346\267\267\345\220\210\350\203\214\345\214\205.java" "b/ACWing/src/dp/\350\203\214\345\214\205\346\250\241\345\236\213/\346\267\267\345\220\210\350\203\214\345\214\205.java" index c75edbd681ba5b26217159a52bbed5f16f3b2620..6ddc404baeb2627167de11ed90d45568b81b63ee 100644 --- "a/ACWing/src/dp/\350\203\214\345\214\205\346\250\241\345\236\213/\346\267\267\345\220\210\350\203\214\345\214\205.java" +++ "b/ACWing/src/dp/\350\203\214\345\214\205\346\250\241\345\236\213/\346\267\267\345\220\210\350\203\214\345\214\205.java" @@ -33,12 +33,13 @@ public class 混合背包 { t[i] = sc.nextInt(); } for (int i = 1; i <= N; i++) { - if (t[i] == 0) { + if (t[i] == 0) {//完全背包 for (int j = v[i]; j <= V; j++) { f[j] = Math.max(f[j], f[j - v[i]] + w[i]); } } else { - if (t[i] == -1) t[i] = 1; + if (t[i] == -1) t[i] = 1;//01背包 + //多重背包转01背包 for (int k = 1; k <= t[i]; k *= 2) { for (int j = V; j >= k * v[i]; j--) {//把k个物品看成可选可不选的一个物品,01背包 f[j] = Math.max(f[j], f[j - k * v[i]] + w[i] * k); diff --git "a/ACWing/src/dp/\350\203\214\345\214\205\346\250\241\345\236\213/\346\275\234\346\260\264\345\221\230\344\272\214\347\273\264\350\264\271\347\224\250\350\203\214\345\214\205.java" "b/ACWing/src/dp/\350\203\214\345\214\205\346\250\241\345\236\213/\346\275\234\346\260\264\345\221\230\344\272\214\347\273\264\350\264\271\347\224\250\350\203\214\345\214\205.java" index ab36ef62bf796fcc20cdeb417d8687e7c4062b0c..477c9f05914d87c302b01b1dd6e9a5f6578be52d 100644 --- "a/ACWing/src/dp/\350\203\214\345\214\205\346\250\241\345\236\213/\346\275\234\346\260\264\345\221\230\344\272\214\347\273\264\350\264\271\347\224\250\350\203\214\345\214\205.java" +++ "b/ACWing/src/dp/\350\203\214\345\214\205\346\250\241\345\236\213/\346\275\234\346\260\264\345\221\230\344\272\214\347\273\264\350\264\271\347\224\250\350\203\214\345\214\205.java" @@ -35,6 +35,7 @@ import java.util.Scanner; * 4 20 119 * 【输出样例】 * 249 + * 最低状态定义稍有区别,状态定义恰好和至少的区别做法就不一样 * 显然三个东西,看如何划分: * 物品: 氧气 氮气 重量 * a b c @@ -51,6 +52,7 @@ import java.util.Scanner; * 在状态定义恰好是j,恰好是k的时候 * f[0,0,0]=0 是合法的 * f[0,j,k]=是非法的,因为只选0个不可能达到恰好是j,或者k + * 应该f[0,j,k]=正无穷 */ public class 潜水员二维费用背包 { public static void main(String[] args) { @@ -59,27 +61,27 @@ public class 潜水员二维费用背包 { m = sc.nextInt();//需要的氮气 shu = sc.nextInt();//有多少个气缸 for (int i = 1; i <= shu; i++) { - o[i] = sc.nextInt(); - d[i] = sc.nextInt(); + v1[i] = sc.nextInt(); + v2[i] = sc.nextInt(); w[i] = sc.nextInt(); } two(); } static int n, m, shu; - static int[] o = new int[1010], d = new int[1010], w = new int[1010]; + static int[] v1 = new int[1010], v2 = new int[1010], w = new int[1010]; static int[][][] f = new int[23][88][1010]; static int[][] dp = new int[5000][1600]; static void two() { - for (int i = 0; i < dp.length; i++) { - Arrays.fill(dp[i], Integer.MAX_VALUE - 10000); + for (int[] ints : dp) { + Arrays.fill(ints, 0x3f3f3f3f); } dp[0][0] = 0; for (int i = 1; i <= shu; i++) { for (int j = n; j >= 0; j--) { for (int k = m; k >= 0; k--) { - dp[j][k] = Math.min(dp[j][k], dp[Math.max(j - o[i], 0)][Math.max(k - d[i], 0)] + w[i]); + dp[j][k] = Math.min(dp[j][k], dp[Math.max(j - v1[i], 0)][Math.max(k - v2[i], 0)] + w[i]); } } } diff --git "a/ACWing/src/dp/\350\203\214\345\214\205\346\250\241\345\236\213/\350\203\214\345\214\205\351\227\256\351\242\230\346\261\202\345\205\267\344\275\223\346\226\271\346\241\210.java" "b/ACWing/src/dp/\350\203\214\345\214\205\346\250\241\345\236\213/\350\203\214\345\214\205\351\227\256\351\242\230\346\261\202\345\205\267\344\275\223\346\226\271\346\241\210.java" index 7f24610ae0e6e4cd7bfec1bd99609f1bd6d4cbce..567a318a18ef1047d55c2e97e679d0a76894e943 100644 --- "a/ACWing/src/dp/\350\203\214\345\214\205\346\250\241\345\236\213/\350\203\214\345\214\205\351\227\256\351\242\230\346\261\202\345\205\267\344\275\223\346\226\271\346\241\210.java" +++ "b/ACWing/src/dp/\350\203\214\345\214\205\346\250\241\345\236\213/\350\203\214\345\214\205\351\227\256\351\242\230\346\261\202\345\205\267\344\275\223\346\226\271\346\241\210.java" @@ -28,9 +28,15 @@ import java.util.Scanner; * 4 6 * 输出样例: * 1 4 + * f[i,j]=max(f[i-1,j],f[i-1,j-v[i]]+w[i]) + * 其实判断出决策的是哪一项 * 对应图论最短路问题 * f[n-1,m]->f[n,m]边权重为0 * f[n-1,m-v[i]]+w[i]->f[n,m] 边权重为w[i] + * 也就是倒着求,f[n,m]是从哪一条边转移过来的 + * 直接判断是f[n-1,m]==f[n,m]还是f[n-1,m-v[i]]+w[i]==f[n,m] + * 如果都相等,说明可选第i号物品,如果只有右边相等说明必选,如果只有左边相等说明不能选 + * 就能判断决策 * 要求字典序最小: * 可能最大价值有多种方案 * 对于一个物品:有三种情况,必选,可选,不能选 @@ -47,14 +53,14 @@ public class 背包问题求具体方案 { v[i] = sc.nextInt(); w[i] = sc.nextInt(); } - for (int i = n; i >= 1; i--) { + for (int i = n; i >= 1; i--) {//倒序枚举每个物品,方便倒推方案 for (int j = 0; j <= m; j++) { f[i][j] = f[i + 1][j]; if (j >= v[i]) f[i][j] = Math.max(f[i][j], f[i + 1][j - v[i]] + w[i]); } } - //f[1][m]是最大值 + //f[1][m]是最大值,是结果 int j = m; for (int i = 1; i <= n; i++) {//1~n个物品可选 if (j >= v[i] && f[i][j] == f[i + 1][j - v[i]] + w[i]) { diff --git "a/ACWing/src/dp/\350\203\214\345\214\205\346\250\241\345\236\213/\350\203\214\345\214\205\351\227\256\351\242\230\346\261\202\346\226\271\346\241\210\346\225\260\351\207\217.java" "b/ACWing/src/dp/\350\203\214\345\214\205\346\250\241\345\236\213/\350\203\214\345\214\205\351\227\256\351\242\230\346\261\202\346\226\271\346\241\210\346\225\260\351\207\217.java" new file mode 100644 index 0000000000000000000000000000000000000000..425f9301ee71756d1c625a627f096278ec75099f --- /dev/null +++ "b/ACWing/src/dp/\350\203\214\345\214\205\346\250\241\345\236\213/\350\203\214\345\214\205\351\227\256\351\242\230\346\261\202\346\226\271\346\241\210\346\225\260\351\207\217.java" @@ -0,0 +1,79 @@ +package dp.背包模型; + +import java.util.Arrays; +import java.util.Scanner; + +/** + * 有 N 件物品和一个容量是 V 的背包。每件物品只能使用一次。 + * 第 i 件物品的体积是 vi,价值是 wi。 + * 求解将哪些物品装入背包,可使这些物品的总体积不超过背包容量,且总价值最大。 + * 输出 最优选法的方案数。注意答案可能很大,请输出答案模 10^9+7 的结果。 + * 输入格式 + * 第一行两个整数,N,V,用空格隔开,分别表示物品数量和背包容积。 + * 接下来有 N 行,每行两个整数 vi,wi,用空格隔开,分别表示第 i 件物品的体积和价值。 + * 输出格式 + * 输出一个整数,表示 方案数 模 109+7 的结果。 + * 数据范围 + * 0通过各种路径到(n,m)最短路径的条数 + * f[i,j]=max ( f[i-1,j] f[i-1,j-v[i]]+w[i] ) + * 最优解,最短路的,起点到最后一条边的方案数累加到终点 + * 当然有可能又多条最短路 + * g[i,j]代表f[i,j]取最大值的数量 + * g[i,j]的计算对于f[i-1,j]的转移就是g[i-1,j] + * 对于f[i-1,j-v]+w的转移g[i-1,j-v] + * 同时转移:g[i-1,j]+g[i-1,j-v] + */ +public class 背包问题求方案数量 { + static int mod = (int) (1e9 + 7); + static int n, m; + static int[] f = new int[1010]; + static int[] g = new int[1010];//存的的恰好 + static int[] v = new int[1010]; + static int[] w = new int[1010]; + + public static void main(String[] args) { + Scanner sc = new Scanner(System.in); + n = sc.nextInt(); + m = sc.nextInt(); + Arrays.fill(f, -0x3f3f3f3f); + f[0] = 0; + g[0] = 1; + 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 = m; j >= v[i]; j--) { + int maxv = Math.max(f[j], f[j - v[i]] + w[i]); + int cnt = 0; + if (maxv == f[j]) cnt += g[j]; + if (maxv == f[j - v[i]] + w[i]) cnt += g[j - v[i]]; + g[j] = cnt % mod; + f[j] = maxv; + } + } + int res = 0; + for (int i = 0; i <= m; i++) { + res = Math.max(res, f[i]); + } + int cnt = 0; + for (int i = 0; i <= m; i++) { + if (res == f[i]) { + cnt = (cnt + g[i]) % mod; + } + } + System.out.println(cnt); + } +}