Algorithm and Data Structure Interview Guide - Complete Knapsack Problem

complete knapsack problem

In this section, we first solve another common knapsack problem: the complete knapsack problem, and then look at a special case of it: change exchange.

Complete backpack

!!! question

给定 $n$ 个物品,第 $i$ 个物品的重量为 $wgt[i-1]$、价值为 $val[i-1]$ ,和一个容量为 $cap$ 的背包。**每个物品可以重复选取**,问在不超过背包容量下能放入物品的最大价值。

Insert image description here

Dynamic programming ideas

The complete knapsack problem is very similar to the 0-1 knapsack problem, the only difference is that there is no limit on the number of item selections .

  • In a 0-1 backpack, there is only one of each item, so item iiAfter i is put into the backpack, it can only go back to i − 1 i-1iChoose from 1 item.
  • In a complete backpack, there are an infinite number of each item, so item iii After putting it in the backpack,you can still go back iiChoose from i items.

Under the stipulation of the complete knapsack, the state [i, c] [i, c][i,c ] changes are divided into two situations.

  • No items placed iii : Same as 0-1 backpack, transferred to[i − 1, c] [i-1, c][i1,c]
  • Place itemsiii : Different from the 0-1 knapsack, transfer to[i, c − wgt [i − 1]] [i, c-wgt[i-1]][i,cwgt[i1]]

Thus the state transition equation becomes:

d p [ i , c ] = max ⁡ ( d p [ i − 1 , c ] , d p [ i , c − w g t [ i − 1 ] ] + v a l [ i − 1 ] ) dp[i, c] = \max(dp[i-1, c], dp[i, c - wgt[i-1]] + val[i-1]) dp[i,c]=max(dp[i1,c],dp[i,cwgt[i1]]+val[i1])

Code

Comparing the codes of the two questions, there is a state transition from i − 1 i-1i1 becomesiii , the rest are completely consistent.

=== “Python”

```python title="unbounded_knapsack.py"
[class]{}-[func]{unbounded_knapsack_dp}
```

=== “C++”

```cpp title="unbounded_knapsack.cpp"
[class]{}-[func]{unboundedKnapsackDP}
```

=== “Java”

```java title="unbounded_knapsack.java"
[class]{unbounded_knapsack}-[func]{unboundedKnapsackDP}
```

=== “C#”

```csharp title="unbounded_knapsack.cs"
[class]{unbounded_knapsack}-[func]{unboundedKnapsackDP}
```

=== “Go”

```go title="unbounded_knapsack.go"
[class]{}-[func]{unboundedKnapsackDP}
```

=== “Swift”

```swift title="unbounded_knapsack.swift"
[class]{}-[func]{unboundedKnapsackDP}
```

=== “JS”

```javascript title="unbounded_knapsack.js"
[class]{}-[func]{unboundedKnapsackDP}
```

=== “TS”

```typescript title="unbounded_knapsack.ts"
[class]{}-[func]{unboundedKnapsackDP}
```

=== “Dart”

```dart title="unbounded_knapsack.dart"
[class]{}-[func]{unboundedKnapsackDP}
```

=== “Rust”

```rust title="unbounded_knapsack.rs"
[class]{}-[func]{unbounded_knapsack_dp}
```

=== “C”

```c title="unbounded_knapsack.c"
[class]{}-[func]{unboundedKnapsackDP}
```

=== “Zig”

```zig title="unbounded_knapsack.zig"
[class]{}-[func]{unboundedKnapsackDP}
```

space optimization

Since the current state is transferred from the left and upper states, dp dp should be optimized after space optimizationEach row in the d p table is traversed in positive order .

This traversal order is exactly the opposite of the 0-1 knapsack. Please use the image below to understand the difference between the two.

=== “<1>”
Insert image description here

=== “<2>”
Insert image description here

=== “<3>”
Insert image description here

=== “<4>”
Insert image description here

=== “<5>”
Insert image description here

=== “<6>”
Insert image description here

The code implementation is relatively simple and only needs to dpdelete the first dimension of the array.

=== “Python”

```python title="unbounded_knapsack.py"
[class]{}-[func]{unbounded_knapsack_dp_comp}
```

=== “C++”

```cpp title="unbounded_knapsack.cpp"
[class]{}-[func]{unboundedKnapsackDPComp}
```

=== “Java”

```java title="unbounded_knapsack.java"
[class]{unbounded_knapsack}-[func]{unboundedKnapsackDPComp}
```

=== “C#”

```csharp title="unbounded_knapsack.cs"
[class]{unbounded_knapsack}-[func]{unboundedKnapsackDPComp}
```

=== “Go”

```go title="unbounded_knapsack.go"
[class]{}-[func]{unboundedKnapsackDPComp}
```

=== “Swift”

```swift title="unbounded_knapsack.swift"
[class]{}-[func]{unboundedKnapsackDPComp}
```

=== “JS”

```javascript title="unbounded_knapsack.js"
[class]{}-[func]{unboundedKnapsackDPComp}
```

=== “TS”

```typescript title="unbounded_knapsack.ts"
[class]{}-[func]{unboundedKnapsackDPComp}
```

=== “Dart”

```dart title="unbounded_knapsack.dart"
[class]{}-[func]{unboundedKnapsackDPComp}
```

=== “Rust”

```rust title="unbounded_knapsack.rs"
[class]{}-[func]{unbounded_knapsack_dp_comp}
```

=== “C”

```c title="unbounded_knapsack.c"
[class]{}-[func]{unboundedKnapsackDPComp}
```

=== “Zig”

```zig title="unbounded_knapsack.zig"
[class]{}-[func]{unboundedKnapsackDPComp}
```

Change exchange problem

The knapsack problem is a representative of a large class of dynamic programming problems, which has many variants, such as the change exchange problem.

!!! question

给定 $n$ 种硬币,第 $i$ 种硬币的面值为 $coins[i - 1]$ ,目标金额为 $amt$ ,**每种硬币可以重复选取**,问能够凑出目标金额的最少硬币个数。如果无法凑出目标金额则返回 $-1$ 。

Insert image description here

Dynamic programming ideas

Change exchange can be regarded as a special case of complete backpack . The two have the following connections and differences.

  • The two questions can be converted into each other, "item" corresponds to "coin", "item weight" corresponds to "coin face value", and "backpack capacity" corresponds to "target amount".
  • The optimization goals are opposite. The knapsack problem is to maximize the value of items, and the change exchange problem is to minimize the number of coins.
  • The knapsack problem is to find a solution that "does not exceed" the capacity of the knapsack, and the change exchange is to find a solution that "exactly" collects the target amount.

Step 1: Think about the decision-making in each round, define the state, and get dp dpd p ​​table

状态[ i , a ] [i, a][i,The sub-problem corresponding to a ] is: former iii coins can make up the amountaaThe minimum number of coins for a is recorded asdp [i, a] dp[i, a]dp[i,a]

two-dimensional dp dpThe size of the d p table is( n + 1 ) × ( amt + 1 ) (n+1) \times (amt+1)(n+1)×(amt+1)

Step 2: Find the optimal substructure and derive the state transition equation

There are the following two differences between this problem and the state transition equation of the complete knapsack.

  • This question requires a minimum value, so the operator max ⁡ ( ) \max() needs to bemax ( ) changes tomin ⁡ ( ) \min()min()
  • The optimization subject is the number of coins rather than the value of the product, so + 1 +1 is executed when the coin is selected.+ 1 is enough.

d p [ i , a ] = min ⁡ ( d p [ i − 1 , a ] , d p [ i , a − c o i n s [ i − 1 ] ] + 1 ) dp[i, a] = \min(dp[i-1, a], dp[i, a - coins[i-1]] + 1) dp[i,a]=min(dp[i1,a],dp[i,acoins[i1]]+1)

Step 3: Determine boundary conditions and state transition sequence

When the target amount is 0 0When 0 , the minimum number of coins to collect it is0 00 , that is, alldp [i, 0] dp[i, 0]dp[i,0 ] are equal to0 00

When there are no coins, it is impossible to collect any > 0 > 0>A target amount of 0 is an invalid solution. In order to make min ⁡ ( ) \min()in the state transition equationThe min ( ) function can identify and filter invalid solutions, we consider using+ ∞ + \infty+ to represent them, that is, alldp [ 0 , a ] dp[0, a]dp[0,a ] are equal to+ ∞ + \infty+

Code

+∞ + \infty is not provided by most programming languages+ variable, onlyintthe maximum value of the integer can be used instead. This in turn leads to large numbers out of bounds: + 1 + 1in the state transition equation+ 1 operation may overflow.

To do this we take the number amt + 1 amt + 1amt+1 represents an invalid solution, becauseamt amtThe maximum number of coins am t isamt amtam t .

Before finally returning, judge dp [ n , amt ] dp[n, amt]dp[n,Is am t ] equal toamt + 1 amt + 1amt+1 , if so, return− 1 -11 means that the target amount cannot be collected.

=== “Python”

```python title="coin_change.py"
[class]{}-[func]{coin_change_dp}
```

=== “C++”

```cpp title="coin_change.cpp"
[class]{}-[func]{coinChangeDP}
```

=== “Java”

```java title="coin_change.java"
[class]{coin_change}-[func]{coinChangeDP}
```

=== “C#”

```csharp title="coin_change.cs"
[class]{coin_change}-[func]{coinChangeDP}
```

=== “Go”

```go title="coin_change.go"
[class]{}-[func]{coinChangeDP}
```

=== “Swift”

```swift title="coin_change.swift"
[class]{}-[func]{coinChangeDP}
```

=== “JS”

```javascript title="coin_change.js"
[class]{}-[func]{coinChangeDP}
```

=== “TS”

```typescript title="coin_change.ts"
[class]{}-[func]{coinChangeDP}
```

=== “Dart”

```dart title="coin_change.dart"
[class]{}-[func]{coinChangeDP}
```

=== “Rust”

```rust title="coin_change.rs"
[class]{}-[func]{coin_change_dp}
```

=== “C”

```c title="coin_change.c"
[class]{}-[func]{coinChangeDP}
```

=== “Zig”

```zig title="coin_change.zig"
[class]{}-[func]{coinChangeDP}
```

The figure below shows the dynamic programming process of change exchange, which is very similar to the complete backpack.

=== “<1>”
Insert image description here

=== “<2>”
Insert image description here

=== “<3>”
Insert image description here

=== “<4>”
Insert image description here

=== “<5>”
Insert image description here

=== “<6>”
Insert image description here

=== “<7>”
Insert image description here

=== “<8>”
Insert image description here

=== “<9>”
Insert image description here

=== “<10>”
Insert image description here

=== “<11>”
Insert image description here

=== “<12>”
Insert image description here

=== “<13>”
Insert image description here

=== “<14>”
Insert image description here

=== “<15>”
Insert image description here

space optimization

The space optimization of change exchange is handled in the same way as the complete backpack.

=== “Python”

```python title="coin_change.py"
[class]{}-[func]{coin_change_dp_comp}
```

=== “C++”

```cpp title="coin_change.cpp"
[class]{}-[func]{coinChangeDPComp}
```

=== “Java”

```java title="coin_change.java"
[class]{coin_change}-[func]{coinChangeDPComp}
```

=== “C#”

```csharp title="coin_change.cs"
[class]{coin_change}-[func]{coinChangeDPComp}
```

=== “Go”

```go title="coin_change.go"
[class]{}-[func]{coinChangeDPComp}
```

=== “Swift”

```swift title="coin_change.swift"
[class]{}-[func]{coinChangeDPComp}
```

=== “JS”

```javascript title="coin_change.js"
[class]{}-[func]{coinChangeDPComp}
```

=== “TS”

```typescript title="coin_change.ts"
[class]{}-[func]{coinChangeDPComp}
```

=== “Dart”

```dart title="coin_change.dart"
[class]{}-[func]{coinChangeDPComp}
```

=== “Rust”

```rust title="coin_change.rs"
[class]{}-[func]{coin_change_dp_comp}
```

=== “C”

```c title="coin_change.c"
[class]{}-[func]{coinChangeDPComp}
```

=== “Zig”

```zig title="coin_change.zig"
[class]{}-[func]{coinChangeDPComp}
```

Change exchange problem II

!!! question

给定 $n$ 种硬币,第 $i$ 种硬币的面值为 $coins[i - 1]$ ,目标金额为 $amt$ ,每种硬币可以重复选取,**问在凑出目标金额的硬币组合数量**。

Insert image description here

Dynamic programming ideas

Compared with the previous question, the goal of this question is the number of combinations, so the sub-question becomes: former iii coins can make up the amountaaThe number of combinations of a . Anddp dpThe d p table is still of size(n + 1) × (amt + 1) (n+1) \times (amt + 1)(n+1)×(amt+1 ) is a two-dimensional matrix.

The number of combinations of the current state is equal to the sum of the number of combinations of the two decisions of not choosing the current coin and choosing the current coin. The state transition equation is:

d p [ i , a ] = d p [ i − 1 , a ] + d p [ i , a − c o i n s [ i − 1 ] ] dp[i, a] = dp[i-1, a] + dp[i, a - coins[i-1]] dp[i,a]=dp[i1,a]+dp[i,acoins[i1]]

When the target amount is 0 0When 0 , the target amount can be collected without selecting any coins, so alldp [i, 0] dp[i, 0]dp[i,0 ] are initialized to1 11 . When there are no coins, it is impossible to collect any> 0 > 0>The target amount is 0 , so all dpin the first rowdp[0,a ] are equal to0 00

Code

=== “Python”

```python title="coin_change_ii.py"
[class]{}-[func]{coin_change_ii_dp}
```

=== “C++”

```cpp title="coin_change_ii.cpp"
[class]{}-[func]{coinChangeIIDP}
```

=== “Java”

```java title="coin_change_ii.java"
[class]{coin_change_ii}-[func]{coinChangeIIDP}
```

=== “C#”

```csharp title="coin_change_ii.cs"
[class]{coin_change_ii}-[func]{coinChangeIIDP}
```

=== “Go”

```go title="coin_change_ii.go"
[class]{}-[func]{coinChangeIIDP}
```

=== “Swift”

```swift title="coin_change_ii.swift"
[class]{}-[func]{coinChangeIIDP}
```

=== “JS”

```javascript title="coin_change_ii.js"
[class]{}-[func]{coinChangeIIDP}
```

=== “TS”

```typescript title="coin_change_ii.ts"
[class]{}-[func]{coinChangeIIDP}
```

=== “Dart”

```dart title="coin_change_ii.dart"
[class]{}-[func]{coinChangeIIDP}
```

=== “Rust”

```rust title="coin_change_ii.rs"
[class]{}-[func]{coin_change_ii_dp}
```

=== “C”

```c title="coin_change_ii.c"
[class]{}-[func]{coinChangeIIDP}
```

=== “Zig”

```zig title="coin_change_ii.zig"
[class]{}-[func]{coinChangeIIDP}
```

space optimization

Spatial optimization is handled in the same way, just delete the coin dimension.

=== “Python”

```python title="coin_change_ii.py"
[class]{}-[func]{coin_change_ii_dp_comp}
```

=== “C++”

```cpp title="coin_change_ii.cpp"
[class]{}-[func]{coinChangeIIDPComp}
```

=== “Java”

```java title="coin_change_ii.java"
[class]{coin_change_ii}-[func]{coinChangeIIDPComp}
```

=== “C#”

```csharp title="coin_change_ii.cs"
[class]{coin_change_ii}-[func]{coinChangeIIDPComp}
```

=== “Go”

```go title="coin_change_ii.go"
[class]{}-[func]{coinChangeIIDPComp}
```

=== “Swift”

```swift title="coin_change_ii.swift"
[class]{}-[func]{coinChangeIIDPComp}
```

=== “JS”

```javascript title="coin_change_ii.js"
[class]{}-[func]{coinChangeIIDPComp}
```

=== “TS”

```typescript title="coin_change_ii.ts"
[class]{}-[func]{coinChangeIIDPComp}
```

=== “Dart”

```dart title="coin_change_ii.dart"
[class]{}-[func]{coinChangeIIDPComp}
```

=== “Rust”

```rust title="coin_change_ii.rs"
[class]{}-[func]{coin_change_ii_dp_comp}
```

=== “C”

```c title="coin_change_ii.c"
[class]{}-[func]{coinChangeIIDPComp}
```

=== “Zig”

```zig title="coin_change_ii.zig"
[class]{}-[func]{coinChangeIIDPComp}
```

Guess you like

Origin blog.csdn.net/zy_dreamer/article/details/132924920