This commit is contained in:
programmercarl
2023-11-09 14:32:56 +08:00
parent 4dac467907
commit 2caee6fe9f
12 changed files with 618 additions and 1266 deletions

View File

@@ -7,6 +7,8 @@
# 动态规划:关于多重背包,你该了解这些!
本题力扣上没有原题,大家可以去[卡码网第56题](https://kamacoder.com/problempage.php?pid=1066)去练习,题意是一样的。
之前我们已经系统的讲解了01背包和完全背包如果没有看过的录友建议先把如下三篇文章仔细阅读一波。
* [动态规划关于01背包问题你该了解这些](https://programmercarl.com/背包理论基础01背包-1.html)
@@ -53,79 +55,96 @@
毫无区别这就转成了一个01背包问题了且每个物品只用一次。
这种方式来实现多重背包的代码如下:
练习题目:[卡码网第56题多重背包](https://kamacoder.com/problempage.php?pid=1066)
代码如下:
```CPP
void test_multi_pack() {
vector<int> weight = {1, 3, 4};
vector<int> value = {15, 20, 30};
vector<int> nums = {2, 3, 2};
int bagWeight = 10;
for (int i = 0; i < nums.size(); i++) {
while (nums[i] > 1) { // nums[i]保留到1把其他物品都展开
// 超时了
#include<iostream>
#include<vector>
using namespace std;
int main() {
int bagWeight,n;
cin >> bagWeight >> n;
vector<int> weight(n, 0);
vector<int> value(n, 0);
vector<int> nums(n, 0);
for (int i = 0; i < n; i++) cin >> weight[i];
for (int i = 0; i < n; i++) cin >> value[i];
for (int i = 0; i < n; i++) cin >> nums[i];
for (int i = 0; i < n; i++) {
while (nums[i] > 1) { // 物品数量不是一的,都展开
weight.push_back(weight[i]);
value.push_back(value[i]);
nums[i]--;
}
}
vector<int> dp(bagWeight + 1, 0);
for(int i = 0; i < weight.size(); i++) { // 遍历物品
for(int i = 0; i < weight.size(); i++) { // 遍历物品注意此时的物品数量不是n
for(int j = bagWeight; j >= weight[i]; j--) { // 遍历背包容量
dp[j] = max(dp[j], dp[j - weight[i]] + value[i]);
}
for (int j = 0; j <= bagWeight; j++) {
cout << dp[j] << " ";
}
cout << endl;
}
cout << dp[bagWeight] << endl;
}
int main() {
test_multi_pack();
}
```
* 时间复杂度O(m × n × k)m物品种类个数n背包容量k单类物品数量
大家去提交之后,发现这个解法超时了,为什么呢,哪里耗时呢?
也有另一种实现方式就是把每种商品遍历的个数放在01背包里面在遍历一遍。
耗时就在 这段代码:
```CPP
for (int i = 0; i < n; i++) {
while (nums[i] > 1) { // 物品数量不是一的,都展开
weight.push_back(weight[i]);
value.push_back(value[i]);
nums[i]--;
}
}
```
如果物品数量很多的话C++中这种操作十分费时主要消耗在vector的动态底层扩容上。其实这里也可以优化先把 所有物品数量都计算好一起申请vector的空间。
这里也有另一种实现方式就是把每种商品遍历的个数放在01背包里面在遍历一遍。
代码如下:(详看注释)
```CPP
#include<iostream>
#include<vector>
using namespace std;
int main() {
int bagWeight,n;
cin >> bagWeight >> n;
vector<int> weight(n, 0);
vector<int> value(n, 0);
vector<int> nums(n, 0);
for (int i = 0; i < n; i++) cin >> weight[i];
for (int i = 0; i < n; i++) cin >> value[i];
for (int i = 0; i < n; i++) cin >> nums[i];
```CPP
void test_multi_pack() {
vector<int> weight = {1, 3, 4};
vector<int> value = {15, 20, 30};
vector<int> nums = {2, 3, 2};
int bagWeight = 10;
vector<int> dp(bagWeight + 1, 0);
for(int i = 0; i < weight.size(); i++) { // 遍历物品
for(int i = 0; i < n; i++) { // 遍历物品
for(int j = bagWeight; j >= weight[i]; j--) { // 遍历背包容量
// 以上为01背包然后加一个遍历个数
for (int k = 1; k <= nums[i] && (j - k * weight[i]) >= 0; k++) { // 遍历个数
dp[j] = max(dp[j], dp[j - k * weight[i]] + k * value[i]);
}
}
// 打印一下dp数组
for (int j = 0; j <= bagWeight; j++) {
cout << dp[j] << " ";
}
cout << endl;
}
cout << dp[bagWeight] << endl;
}
int main() {
test_multi_pack();
}
```
* 时间复杂度O(m × n × k)m物品种类个数n背包容量k单类物品数量
时间复杂度O(m × n × k)m物品种类个数n背包容量k单类物品数量
从代码里可以看出是01背包里面在加一个for循环遍历一个每种商品的数量。 和01背包还是如出一辙的。
@@ -146,320 +165,14 @@ int main() {
### Java
```Java
public void testMultiPack1(){
// 版本一改变物品数量为01背包格式
List<Integer> weight = new ArrayList<>(Arrays.asList(1, 3, 4));
List<Integer> value = new ArrayList<>(Arrays.asList(15, 20, 30));
List<Integer> nums = new ArrayList<>(Arrays.asList(2, 3, 2));
int bagWeight = 10;
for (int i = 0; i < nums.size(); i++) {
while (nums.get(i) > 1) { // 把物品展开为i
weight.add(weight.get(i));
value.add(value.get(i));
nums.set(i, nums.get(i) - 1);
}
}
int[] dp = new int[bagWeight + 1];
for(int i = 0; i < weight.size(); i++) { // 遍历物品
for(int j = bagWeight; j >= weight.get(i); j--) { // 遍历背包容量
dp[j] = Math.max(dp[j], dp[j - weight.get(i)] + value.get(i));
}
System.out.println(Arrays.toString(dp));
}
}
public void testMultiPack2(){
// 版本二:改变遍历个数
int[] weight = new int[] {1, 3, 4};
int[] value = new int[] {15, 20, 30};
int[] nums = new int[] {2, 3, 2};
int bagWeight = 10;
int[] dp = new int[bagWeight + 1];
for(int i = 0; i < weight.length; i++) { // 遍历物品
for(int j = bagWeight; j >= weight[i]; j--) { // 遍历背包容量
// 以上为01背包然后加一个遍历个数
for (int k = 1; k <= nums[i] && (j - k * weight[i]) >= 0; k++) { // 遍历个数
dp[j] = Math.max(dp[j], dp[j - k * weight[i]] + k * value[i]);
}
System.out.println(Arrays.toString(dp));
}
}
}
```
### Python
改变物品数量为01背包格式无参版
```python
def test_multi_pack():
weight = [1, 3, 4]
value = [15, 20, 30]
nums = [2, 3, 2]
bagWeight = 10
# 将数量大于1的物品展开
for i in range(len(nums)):
while nums[i] > 1:
weight.append(weight[i])
value.append(value[i])
nums[i] -= 1
dp = [0] * (bagWeight + 1)
for i in range(len(weight)): # 遍历物品
for j in range(bagWeight, weight[i] - 1, -1): # 遍历背包容量
dp[j] = max(dp[j], dp[j - weight[i]] + value[i])
for j in range(bagWeight + 1):
print(dp[j], end=" ")
print()
print(dp[bagWeight])
test_multi_pack()
```
改变遍历个数(无参版)
```python
def test_multi_pack():
weight = [1, 3, 4]
value = [15, 20, 30]
nums = [2, 3, 2]
bagWeight = 10
dp = [0] * (bagWeight + 1)
for i in range(len(weight)): # 遍历物品
for j in range(bagWeight, weight[i] - 1, -1): # 遍历背包容量
# 以上为01背包然后加一个遍历个数
for k in range(1, nums[i] + 1): # 遍历个数
if j - k * weight[i] >= 0:
dp[j] = max(dp[j], dp[j - k * weight[i]] + k * value[i])
# 打印一下dp数组
for j in range(bagWeight + 1):
print(dp[j], end=" ")
print()
print(dp[bagWeight])
test_multi_pack()
```
改变物品数量为01背包格式有参版
```python
def test_multi_pack(weight, value, nums, bagWeight):
# 将数量大于1的物品展开
for i in range(len(nums)):
while nums[i] > 1:
weight.append(weight[i])
value.append(value[i])
nums[i] -= 1
dp = [0] * (bagWeight + 1)
for i in range(len(weight)): # 遍历物品
for j in range(bagWeight, weight[i] - 1, -1): # 遍历背包容量
dp[j] = max(dp[j], dp[j - weight[i]] + value[i])
for j in range(bagWeight + 1):
print(dp[j], end=" ")
print()
print(dp[bagWeight])
if __name__ == "__main__":
weight = [1, 3, 4]
value = [15, 20, 30]
nums = [2, 3, 2]
bagWeight = 10
test_multi_pack(weight, value, nums, bagWeight)
```
改变遍历个数(有参版)
```python
def test_multi_pack(weight, value, nums, bagWeight):
dp = [0] * (bagWeight + 1)
for i in range(len(weight)): # 遍历物品
for j in range(bagWeight, weight[i] - 1, -1): # 遍历背包容量
# 以上为01背包然后加一个遍历个数
for k in range(1, nums[i] + 1): # 遍历个数
if j - k * weight[i] >= 0:
dp[j] = max(dp[j], dp[j - k * weight[i]] + k * value[i])
# 使用 join 函数打印 dp 数组
print(' '.join(str(dp[j]) for j in range(bagWeight + 1)))
print(dp[bagWeight])
if __name__ == "__main__":
weight = [1, 3, 4]
value = [15, 20, 30]
nums = [2, 3, 2]
bagWeight = 10
test_multi_pack(weight, value, nums, bagWeight)
```
### Go
```go
package theory
import "log"
// 多重背包可以化解为 01 背包
func multiplePack(weight, value, nums []int, bagWeight int) int {
for i := 0; i < len(nums); i++ {
for nums[i] > 1 {
weight = append(weight, weight[i])
value = append(value, value[i])
nums[i]--
}
}
log.Println(weight)
log.Println(value)
res := make([]int, bagWeight+1)
for i := 0; i < len(weight); i++ {
for j := bagWeight; j >= weight[i]; j-- {
res[j] = getMax(res[j], res[j-weight[i]]+value[i])
}
log.Println(res)
}
return res[bagWeight]
}
```
> 单元测试
```go
package theory
import "testing"
func Test_multiplePack(t *testing.T) {
type args struct {
weight []int
value []int
nums []int
bagWeight int
}
tests := []struct {
name string
args args
want int
}{
{
name: "one",
args: args{
weight: []int{1, 3, 4},
value: []int{15, 20, 30},
nums: []int{2, 3, 2},
bagWeight: 10,
},
want: 90,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := multiplePack(tt.args.weight, tt.args.value, tt.args.nums, tt.args.bagWeight); got != tt.want {
t.Errorf("multiplePack() = %v, want %v", got, tt.want)
}
})
}
}
```
> 输出
```
=== RUN Test_multiplePack
=== RUN Test_multiplePack/one
2022/03/02 21:09:05 [1 3 4 1 3 3 4]
2022/03/02 21:09:05 [15 20 30 15 20 20 30]
2022/03/02 21:09:05 [0 15 15 15 15 15 15 15 15 15 15]
2022/03/02 21:09:05 [0 15 15 20 35 35 35 35 35 35 35]
2022/03/02 21:09:05 [0 15 15 20 35 45 45 50 65 65 65]
2022/03/02 21:09:05 [0 15 30 30 35 50 60 60 65 80 80]
2022/03/02 21:09:05 [0 15 30 30 35 50 60 60 70 80 80]
2022/03/02 21:09:05 [0 15 30 30 35 50 60 60 70 80 80]
2022/03/02 21:09:05 [0 15 30 30 35 50 60 60 70 80 90]
--- PASS: Test_multiplePack (0.00s)
--- PASS: Test_multiplePack/one (0.00s)
PASS
```
### TypeScript
> 版本一(改变数据源):
```typescript
function testMultiPack() {
const bagSize: number = 10;
const weightArr: number[] = [1, 3, 4],
valueArr: number[] = [15, 20, 30],
amountArr: number[] = [2, 3, 2];
for (let i = 0, length = amountArr.length; i < length; i++) {
while (amountArr[i] > 1) {
weightArr.push(weightArr[i]);
valueArr.push(valueArr[i]);
amountArr[i]--;
}
}
const goodsNum: number = weightArr.length;
const dp: number[] = new Array(bagSize + 1).fill(0);
// 遍历物品
for (let i = 0; i < goodsNum; i++) {
// 遍历背包容量
for (let j = bagSize; j >= weightArr[i]; j--) {
dp[j] = Math.max(dp[j], dp[j - weightArr[i]] + valueArr[i]);
}
}
console.log(dp);
}
testMultiPack();
```
> 版本二(改变遍历方式):
```typescript
function testMultiPack() {
const bagSize: number = 10;
const weightArr: number[] = [1, 3, 4],
valueArr: number[] = [15, 20, 30],
amountArr: number[] = [2, 3, 2];
const goodsNum: number = weightArr.length;
const dp: number[] = new Array(bagSize + 1).fill(0);
// 遍历物品
for (let i = 0; i < goodsNum; i++) {
// 遍历物品个数
for (let j = 0; j < amountArr[i]; j++) {
// 遍历背包容量
for (let k = bagSize; k >= weightArr[i]; k--) {
dp[k] = Math.max(dp[k], dp[k - weightArr[i]] + valueArr[i]);
}
}
}
console.log(dp);
}
testMultiPack();
```