diff --git a/TangDou/AcWing/BeiBao/1020.md b/TangDou/AcWing/BeiBao/1020.md index ea70e3e..659f00c 100644 --- a/TangDou/AcWing/BeiBao/1020.md +++ b/TangDou/AcWing/BeiBao/1020.md @@ -79,54 +79,38 @@ $f(i,j,k)$ 表示从前$i$个物品中选,且花费$1$**不少于**$j$,花费$ 有普通的二维费用背包问题中,$j,k$是不能进行超载的,超过了背包就太重, 背包就 **漏** 了! -在本题中,是 **可以超载** 的,理解一下超载是什么意思: -> - $j$:氧气还需缺少$j$升 -> - $k$:氮气还需缺少$k$升 -> 举栗子:$j=2,k=5$,就是氧气还需要$2$升,氮气还需要$5$升,现在出现的某个气瓶,氧气$20$升,氮气$50$升,一个就可以把你的需求满足,那么请问:你还需要氧气多少升、氮气多少升呢? -> **答**:不需要,都可以满足要求了,即$j=0,k=0$,也就是$f[i-1][0][0]+w$,而对于一个无欲无求的$f[i-1][0][0]$自然是等于$0$,也就是$f[i][j][k]=w$ +在本题中是 **可以超载** 的,理解一下超载是什么意思: +> - $j$:氧气还需要$j$升 +> - $k$:氮气还需要$k$升 + +**举栗子**:$j=2,k=5$,就是氧气还需要$2$升,氮气还需要$5$升,现在出现的某个气瓶,氧气$20$升,氮气$50$升,一个就可以把你的需求满足,那么请问:你还需要氧气多少升、氮气多少升呢? -### 三、三维朴素解法 +**答**:不需要,都可以满足要求了,即$j=0,k=0$,也就是$f[i-1][0][0]+w$,而对于一个无欲无求的$f[i-1][0][0]$自然是等于$0$,也就是$f[i][j][k]=w$ + +### 三、三维解法 ```cpp {.line-numbers} #include - using namespace std; const int N = 1010; const int M = 110; +int n, m, m1, m2; int f[N][M][M]; -int n, m1, m2; -//二维费用01背包-不少于维度费用,求最小代价 int main() { - //注意次序 - scanf("%d %d %d", &m1, &m2, &n); - //求最小值 + cin >> m1 >> m2 >> n; memset(f, 0x3f, sizeof f); f[0][0][0] = 0; for (int i = 1; i <= n; i++) { int v1, v2, w; - scanf("%d %d %d", &v1, &v2, &w); + cin >> v1 >> v2 >> w; for (int j = 0; j <= m1; j++) for (int k = 0; k <= m2; k++) { - //不选择i号物品 f[i][j][k] = f[i - 1][j][k]; - //分情况讨论 - - //物品i加上就够一维使用,此时,只关心二维情况即可 - if (j - v1 < 0 && k - v2 >= 0) - f[i][j][k] = min(f[i][j][k], f[i - 1][0][k - v2] + w); - //物品i加上就够二维使用,此时,只关心一维情况即可 - else if (j - v1 >= 0 && k - v2 < 0) - f[i][j][k] = min(f[i][j][k], f[i - 1][j - v1][0] + w); - //如果选择了i号物品,两个维度直接拉满,那么只选择一个i就足够用,它参选的价值是w - else if (j - v1 < 0 && k - v2 < 0) - f[i][j][k] = min(f[i][j][k], w); - else - //正常递推 - f[i][j][k] = min(f[i][j][k], f[i - 1][j - v1][k - v2] + w); + f[i][j][k] = min(f[i - 1][j][k], f[i - 1][max(0, j - v1)][max(0, k - v2)] + w); } } - printf("%d\n", f[n][m1][m2]); + cout << f[n][m1][m2] << endl; return 0; } ``` diff --git a/TangDou/AcWing/BeiBao/1020_1.cpp b/TangDou/AcWing/BeiBao/1020_1.cpp index 027baf6..f207c48 100644 --- a/TangDou/AcWing/BeiBao/1020_1.cpp +++ b/TangDou/AcWing/BeiBao/1020_1.cpp @@ -1,16 +1,12 @@ #include - using namespace std; const int N = 1010; const int M = 110; +int n, m, m1, m2; int f[N][M][M]; -int n, m1, m2; -// 二维费用01背包-不少于维度费用,求最小代价 int main() { - // 注意次序 cin >> m1 >> m2 >> n; - // 求最小值 memset(f, 0x3f, sizeof f); f[0][0][0] = 0; @@ -19,24 +15,10 @@ int main() { cin >> v1 >> v2 >> w; for (int j = 0; j <= m1; j++) for (int k = 0; k <= m2; k++) { - // 不选择i号物品 f[i][j][k] = f[i - 1][j][k]; - - // 分情况讨论 - // 物品i加上就够一维使用,此时,只关心二维情况即可 - if (j - v1 < 0 && k - v2 >= 0) - f[i][j][k] = min(f[i][j][k], f[i - 1][0][k - v2] + w); - // 物品i加上就够二维使用,此时,只关心一维情况即可 - else if (j - v1 >= 0 && k - v2 < 0) - f[i][j][k] = min(f[i][j][k], f[i - 1][j - v1][0] + w); - // 如果选择了i号物品,两个维度直接拉满,那么只选择一个i就足够用,它参选的价值是w - else if (j - v1 < 0 && k - v2 < 0) - f[i][j][k] = min(f[i][j][k], w); - else - // 正常递推 - f[i][j][k] = min(f[i][j][k], f[i - 1][j - v1][k - v2] + w); + f[i][j][k] = min(f[i - 1][j][k], f[i - 1][max(0, j - v1)][max(0, k - v2)] + w); } } - printf("%d\n", f[n][m1][m2]); + cout << f[n][m1][m2] << endl; return 0; } \ No newline at end of file diff --git a/TangDou/AcWing/BeiBao/4.md b/TangDou/AcWing/BeiBao/4.md index eb728a0..de039fb 100644 --- a/TangDou/AcWing/BeiBao/4.md +++ b/TangDou/AcWing/BeiBao/4.md @@ -34,14 +34,21 @@ $0 + +* **状态表示** +集合:所有只从前$i$个物品中选,并且总体积不超过$j$的选法 +属性:集合中每一个选法对应的总价值的最大值 + +* **状态计算** +就是一个集合划分的过程,就是和完全背包很像,但不像完全背包有无穷多个,而是有数量限制 + +* 初始状态:`f[0][0]` +* 目标状态:`f[n][m]` + +#### 状态转移方程 +$$\large f[i][j] = max\{(f[i-1][j − k*v[i]] + k*w[i])   |  0 ≤ k ≤ s[i],j>=k*v[i]\}$$ ### 三、实现代码(二维数组) ```cpp {.line-numbers} diff --git a/TangDou/AcWing/BeiBao/5.drawio b/TangDou/AcWing/BeiBao/5.drawio new file mode 100644 index 0000000..4c12db3 --- /dev/null +++ b/TangDou/AcWing/BeiBao/5.drawio @@ -0,0 +1 @@ +5VxNj6M4FPw1lnYP2wJsg32EhPTMoU992DMTnAQNCREhk/Qc9rev7ZgEcFrqlZY8FPd8yDzDI9Qru1xAB+HZ9vxaZ/vNW5WLEgVefkZ4joLAJ0GA1F8v/7hEGDaBdV3kZqdb4L34LUzQM9FjkYtDb8emqsqm2PeDy2q3E8umF8vqujr1d1tVZf+s+2wtrMD7Mivt6N9F3mxM1A/5reObKNYbc2oWRJeObdbubK7ksMny6tQJ4RThWV1VzaW1Pc9EqcBrcbkct/ik9/rBarFrvnLA24rXm49f/PXb0affjz/Z2yv5y2T5lZXH/gUfmo8WAplGoi03ktOmaMT7PluqnpMsuIxtmm0pt3zZzA77SwlWxVnIsyarateYksqy48ScTNSNOH96Ff4VG0kqUW1FU3/IXcwBxKBp6ORHZvt0Kw41oU2nLG0sM3RYXxPfEJMNA9p/ADCwAAymDaDvTwxBbCFIJo4gmxiC1EIQj4jgqijLWVVWtU6MV1T9kfFDU1c/Racn1D8mQyd++fl/anEFdSq1aEdXb0ZNMZKcYbIRoXiOeKganKKYonSBkhniMUpDxGaIcXm9M5lgJgfBjMl/fij/w8HLywtKGUoWiHO1b+yhJNJpYsSIisisie5KsMqt8nHEMUqpTtyeSrZlJJa9gT4qRVxGCEoYYnPdRRBftJGkbXimEccBoumul4wx/aGoOmHMO4deksWqkXiI6+vnCUr052Wpvn7rYw6ZK8nR9CnaZ9qu2okBLU0oK4v1Tm6WYqUyKKIVUtljE94WeV5+NiLq6rjLFf/n3kOEjNi89T1+h7nBaMy11wJBh7m6qElquMcjXV3JZe9KYUn9lsQtB2TpZXHnSJLmRlV7DNhUvTK05ZKbrPADeFrYKxx8pYX3j+91poLrLCJLLEf3hTVzxRc5damC+rrEkY6Y6aRzOEcMq/3VPgsUXyahRDOEqWwxU5nlvCEnk0ueRM+bimCdmdRNqgQhPFXspdyYbmK4EGFLsVzeW4j8YJRQbxTUsQ+93iB3Zu0nw3xoWeBBt1fcY3oWGNDZ1EAP7wjRk4E+tDPwoEcW6N4ftifoaPd1DcD1EoH/OYIeLyWcop6cIvt8UL2AWtUj7KGKzCAVOaeC5eTemGHBD6xvDYygyOB3ALiDigwNejuCnFJkcNDtOwbPr8jgoNt+3J7Sn1Zi8XDiYbbE3ivHaAobuOd5CYYeAqCe9zGrmqHCwoPuoOeFB91BzwsPuu157enFGYUlBFph3fOwhEMPAVAPC6Sw0KC3855TCgsOuoMeFh5028PaoDujsNQDVljsnoeld56WP3YIOPjcFh50UA/7oGUNmxroDnpYeNBtD2sz3R2FjaAV1gEPGwZ90MHfXcAOPIgNh68cQIPerrOeWWKjcGqgO2BiGZ4a6LaJHeOXFyYqsewL7zo9VGKJAybWkljolxGIA09iLYkFB90BF2tJLDjoDrhYS2LBQbddbOiwxEK/60QcdLHkzi9mPnYMOPAodiix4KC3iV2SWHjQHXSx8KDbLjZyV2IJBZZYCupiYST2q8+/xxsDDjyLHUosPOgOulh40EFd7GNAH0osPOi2i2XuSiy985Umj5VYUBcLc6OYQt/JoQ66WHDQW/VxSmLBQQd1sUASCw667WK5wxILfaM4dNDFhtB3ckIHn8XCg+6gi4UH3UEXCw+67WLbN7Bc1NhwvDvFcvP2rfC6r/Pd+jj9Fw== \ No newline at end of file diff --git a/TangDou/AcWing/BeiBao/5.md b/TangDou/AcWing/BeiBao/5.md index 440185c..25ef606 100644 --- a/TangDou/AcWing/BeiBao/5.md +++ b/TangDou/AcWing/BeiBao/5.md @@ -1,25 +1,72 @@ ##[$AcWing$ $5$. 多重背包问题 II](https://www.acwing.com/problem/content/description/5/) -### 一、与朴素版本的区别 + +### 一、题目描述 +有 $N$ 种物品和一个容量是 $V$ 的背包。 + +第 $i$ 种物品最多有 $s_i$ 件,每件体积是 $v_i$,价值是 $w_i$。 + +求解将哪些物品装入背包,可使物品体积总和不超过背包容量,且价值总和最大。输出**最大价值**。 + +**输入格式** +第一行两个整数,$N,V$,用空格隔开,分别表示物品种数和背包容积。 + +接下来有 $N$ 行,每行三个整数 $v_i,w_i,s_i$,用空格隔开,分别表示第 $i$ 种物品的体积、价值和数量。 + +**输出格式** +输出一个整数,表示最大价值。 + +**数据范围** +$0优化思路: -比如我们从一个货车搬百事可乐的易拉罐(因为我爱喝不健康的快乐水~),如果存在$200$个易拉罐,小超市本次要的数量为一个小于$200$的数字$n$,搬的策略是什么呢? +假如$A$物品,有$10$个,你最后要几个不一定,可能是$0$个,$1$个,$2$个,....,$10$个。 -$A$、一个一个搬,直到$n$为止。 +我们可以这样打包: -$B$、在出厂前打成$1$个一箱,$2$个一箱,$4$个一箱,$8$个一箱,$16$个一箱,$32$个一箱,$64$个一箱,乘下$73$个,不够下一轮的$128$个了,该怎么办呢?剩下的打成$73$个一箱! +![](https://dsideal.obs.cn-north-1.myhuaweicloud.com/HuangHai/BlogImages/202403120953334.png) ->为什么要把剩下的$73$个打成一个包呢?不是再分解成$64$,$32$这样的组合呢?这是因为本质是化解为$01$背包,一来这么分解速度最快,二来可以表示原来数量的任何子集。 +上面这样的转化,就是把一个个尝试,转化为了成批说事,比如上面圆圈里的$4$,理解为$i$物品$4$个打成一包,体积是原来的$4$倍,价值也是原来的$4$倍。这个打包完成的新物品,你可以选择,也可以不选择,当你最终方案中$i$物品要了$5$个时,这个打包$4$就被选中了,当你最终方案中$i$物品要了$2$个时,这个打包$4$就被放弃了。 +打包,就是成批讨论,避免了一个个讨论,组团,按$yxc$的说法就是集合,不管怎么样吧,二进制打包法将极大加快计算速度!理由:比如$INT\_MAX$,其实就是$2^{31}$,也就是划分成了最多$31$个包,能不快吗!就是打包费点劲。 ### 三、一维实现代码 【推荐】 ```cpp {.line-numbers} diff --git a/TangDou/AcWing/BeiBao/6.md b/TangDou/AcWing/BeiBao/6.md index 7ba2b39..d1b08ea 100644 --- a/TangDou/AcWing/BeiBao/6.md +++ b/TangDou/AcWing/BeiBao/6.md @@ -1,4 +1,4 @@ -## [AcWing 6. 多重背包问题 III](https://www.acwing.com/problem/content/6/) +## [$AcWing$ $6$. 多重背包问题 $III$](https://www.acwing.com/problem/content/6/) ### 一、题目描述 有 $N$ 种物品和一个容量是 $V$ 的背包。 @@ -40,22 +40,8 @@ $0三种解法的根本区别在于数据范围,题面都是一样的: +### 二、三种解法如何选择 +三种解法的根本区别在于数据范围,题面都是一样的: