mirror of
https://github.com/youngyangyang04/leetcode-master.git
synced 2026-02-02 18:39:09 +08:00
Update
This commit is contained in:
@@ -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();
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user