PROJECTENGRAM

[PS] EMPEROR ARCANA

Emperor Arcana: 0/1 Knapsack & Coin Change

Tags: #◇CSALGCM #◇Problem Sets #◇HackerRank #◇Algorithms #◇Dynamic Programming


◇Overview

The Emperor Arcana represents dynamic programming foundations built on optimal substructure, state memoization, and space-optimized tabulation. This problem set explores two cornerstone problems: The 0/1 Knapsack Problem and The Fewest Coins Change Problem.


◇Problem 1: The Emperor's Vault (0/1 Knapsack)

Problem Description

Given NN imperial relics, each with an integer weight wi>0w_i > 0 and integer value vi>0v_i > 0, and a royal vault cart with maximum carrying capacity WW. Determine the maximum total value of relics that can be transported. Each relic can be taken at most once (00 or 11).

Constraints:

  • 1≤N≤10001 \le N \le 1000
  • 1≤W≤1041 \le W \le 10^4
  • 1≤wi≤W1 \le w_i \le W
  • 1≤vi≤1051 \le v_i \le 10^5

Approach & Intuition: Optimal Substructure

Let DP[i][w]DP[i][w] be the maximum value achievable considering a subset of the first ii relics with maximum capacity limit ww.
For the ii-th relic with weight wiw_i and value viv_i:

  1. Exclude relic ii: The capacity remains ww, giving value DP[i−1][w]DP[i-1][w].
  2. Include relic ii (if w≥wiw \ge w_i): Take relic ii, gaining value viv_i, while remaining capacity becomes w−wiw - w_i, giving value DP[i−1][w−wi]+viDP[i-1][w - w_i] + v_i.

Recurrence Relation

DP[i][w]={0if i=0 or w=0DP[i−1][w]if w<wimax⁡(DP[i−1][w],DP[i−1][w−wi]+vi)if w≥wiDP[i][w] = \begin{cases} 0 & \text{if } i = 0 \text{ or } w = 0 \\ DP[i-1][w] & \text{if } w < w_i \\ \max(DP[i-1][w], DP[i-1][w - w_i] + v_i) & \text{if } w \ge w_i \end{cases}

Stepped Calculation Trace

Consider N=4,W=7N = 4, W = 7:

  • Relic 1: w1=1,v1=1w_1 = 1, v_1 = 1
  • Relic 2: w2=3,v2=4w_2 = 3, v_2 = 4
  • Relic 3: w3=4,v3=5w_3 = 4, v_3 = 5
  • Relic 4: w4=5,v4=7w_4 = 5, v_4 = 7

Tabulation Matrix: DP[i][w]DP[i][w]

Relic ii (wi,viw_i, v_i)w=0w=0w=1w=1w=2w=2w=3w=3w=4w=4w=5w=5w=6w=6w=7w=7
0 (Base)00000000
1 (1, 1)01111111
2 (3, 4)01145555
3 (4, 5)01145669
4 (5, 7)01145789

Optimal Selection Traceback:

  • At DP[4][7]=9==DP[3][7]DP[4][7] = 9 == DP[3][7]: Relic 4 not taken. Move to (3,7)(3, 7).
  • At DP[3][7]=9≠DP[2][7]=5DP[3][7] = 9 \ne DP[2][7] = 5: Relic 3 is taken (v3=5v_3 = 5). Remaining capacity 7−4=37 - 4 = 3. Move to (2,3)(2, 3).
  • At DP[2][3]=4≠DP[1][3]=1DP[2][3] = 4 \ne DP[1][3] = 1: Relic 2 is taken (v2=4v_2 = 4). Remaining capacity 3−3=03 - 3 = 0. Move to (1,0)(1, 0).
  • Selected Relics: Relic 3 and Relic 2. Total Value: 5+4=95 + 4 = 9, Total Weight: 4+3=7≤74 + 3 = 7 \le 7.

Complexity Analysis

  • Time Complexity: O(N⋅W)O(N \cdot W) because each state in the N×WN \times W grid is computed in O(1)O(1) time.
  • Space Complexity:
    • Standard 2D DP: O(N⋅W)O(N \cdot W).
    • Optimized 1D DP: O(W)O(W) by traversing capacity backwards (w=W…wiw = W \dots w_i), ensuring each item is considered at most once.

◇Problem 2: Imperial Coin Exchange (Coin Change Problem)

Problem Description

Given an array of coin denominations C=[c1,c2,…,ck]C = [c_1, c_2, \dots, c_k] and a target amount AA. Find the minimum number of coins needed to make up that amount. If the amount cannot be formed, return −1-1. Each coin denomination has infinite supply (Unbounded).

Constraints:

  • 1≤k≤121 \le k \le 12
  • 1≤ci≤1041 \le c_i \le 10^4
  • 0≤A≤1040 \le A \le 10^4

Approach & Intuition

Let DP[a]DP[a] be the minimum number of coins to make amount aa.
For any target amount a>0a > 0:
DP[a]=1+min⁡ci≤aDP[a−ci]DP[a] = 1 + \min_{c_i \le a} DP[a - c_i]
Base case: DP[0]=0DP[0] = 0. Initialize all DP[a]=∞DP[a] = \infty for a>0a > 0.

Stepped Calculation Trace

Let C=[1,2,5]C = [1, 2, 5] and A=11A = 11:

  • DP[0]=0DP[0] = 0
  • DP[1]=1+DP[0]=1DP[1] = 1 + DP[0] = 1
  • DP[2]=1+min⁡(DP[1],DP[0])=1+0=1DP[2] = 1 + \min(DP[1], DP[0]) = 1 + 0 = 1 (using coin 2)
  • DP[3]=1+min⁡(DP[2],DP[1])=1+1=2DP[3] = 1 + \min(DP[2], DP[1]) = 1 + 1 = 2
  • DP[4]=1+min⁡(DP[3],DP[2])=1+1=2DP[4] = 1 + \min(DP[3], DP[2]) = 1 + 1 = 2
  • DP[5]=1+min⁡(DP[4],DP[3],DP[0])=1+0=1DP[5] = 1 + \min(DP[4], DP[3], DP[0]) = 1 + 0 = 1 (using coin 5)
  • DP[11]=1+min⁡(DP[10],DP[9],DP[6])=1+DP[10]=1+2=3DP[11] = 1 + \min(DP[10], DP[9], DP[6]) = 1 + DP[10] = 1 + 2 = 3 (coins: 5+5+15 + 5 + 1).

◇Complete C++ Implementation

『』
CPP
#include <iostream>
#include <vector>
#include <algorithm>

using namespace std;

// Problem 1: 0/1 Knapsack with 1D Space Optimization
int solveKnapsack01(int W, const vector<int>& weights, const vector<int>& values) {
    int n = weights.size();
    vector<int> dp(W + 1, 0);

    for (int i = 0; i < n; i++) {
        // Traverse backwards to prevent using the same item multiple times
        for (int w = W; w >= weights[i]; w--) {
            dp[w] = max(dp[w], dp[w - weights[i]] + values[i]);
        }
    }
    return dp[W];
}

// Problem 2: Fewest Coins Change
int solveCoinChange(const vector<int>& coins, int amount) {
    const int INF = 1e9;
    vector<int> dp(amount + 1, INF);
    dp[0] = 0;

    for (int a = 1; a <= amount; a++) {
        for (int coin : coins) {
            if (a >= coin && dp[a - coin] != INF) {
                dp[a] = min(dp[a], dp[a - coin] + 1);
            }
        }
    }
    return (dp[amount] >= INF) ? -1 : dp[amount];
}

int main() {
    ios_base::sync_with_stdio(false);
    cin.tie(NULL);

    // Test 0/1 Knapsack
    int W = 7;
    vector<int> weights = {1, 3, 4, 5};
    vector<int> values = {1, 4, 5, 7};
    cout << "Max Knapsack Value: " << solveKnapsack01(W, weights, values) << "\n"; // Output: 9

    // Test Coin Change
    vector<int> coins = {1, 2, 5};
    int amount = 11;
    cout << "Min Coins Needed: " << solveCoinChange(coins, amount) << "\n"; // Output: 3

    return 0;
}

◇References & Navigation