{"id":53,"date":"2026-02-25T23:25:15","date_gmt":"2026-02-25T15:25:15","guid":{"rendered":"https:\/\/blog.mirpri.com\/?p=53"},"modified":"2026-02-25T23:56:25","modified_gmt":"2026-02-25T15:56:25","slug":"the-knapsack-problem","status":"publish","type":"post","link":"https:\/\/blog.mirpri.com\/index.php\/the-knapsack-problem\/","title":{"rendered":"The Knapsack Problem: From 0\/1 to Advanced"},"content":{"rendered":"\n<p>The Knapsack problem is the &#8220;Hello World&#8221; of Dynamic Programming. But beyond the basic 0\/1 version lies a world of clever optimizations and mathematical tricks. In this post, we\u2019ll break down the three major types and the patterns that solve them.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">1. The Foundation: 0\/1 Knapsack<\/h2>\n\n\n\n<p><strong>The Rule:<\/strong> You have one of each item. You either take it (1) or leave it (0).<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">The Logic<\/h3>\n\n\n\n<p>We define <code>dp[i][j]<\/code> as the maximum value using a subset of the first <code>i<\/code> items with a total capacity <code>j<\/code>.<\/p>\n\n\n\n<p>The choice is simple:<\/p>\n\n\n\n<ol start=\"1\" class=\"wp-block-list\">\n<li><strong>Exclude<\/strong> the current item: <code>dp[i-1][j]<\/code><\/li>\n\n\n\n<li><strong>Include<\/strong> the current item: <code>dp[i-1][j - w_i] + v_i<\/code><\/li>\n<\/ol>\n\n\n\n<h3 class=\"wp-block-heading\">The &#8220;Space&#8221; Pro-Tip<\/h3>\n\n\n\n<p>You don&#8217;t need a 2D array. By <strong>iterating backwards<\/strong> through the capacity, you can use a single 1D array.<\/p>\n\n\n\n<p>Just update backward to avoid using the same item twice.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>for item in items:\n    for j in range(W, item.w - 1, -1):\n        dp&#91;j] = max(dp&#91;j], dp&#91;j - item.w] + item.v)\n<\/code><\/pre>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">2. The Buffet: Complete Knapsack<\/h2>\n\n\n\n<p><strong>The Rule:<\/strong> Items are infinite. Take as many as you want.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">The Transformation<\/h3>\n\n\n\n<p>If you can take an item multiple times, the state $dp[j]$ should depend on the <strong>current<\/strong> item&#8217;s updated state, not the previous one.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">The Simple Twist<\/h3>\n\n\n\n<p>The code is almost identical to 0\/1 Knapsack, but we <strong>iterate forwards<\/strong>.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>for item in items:\n    for j in range(item.w, W + 1):\n        dp&#91;j] = max(dp&#91;j], dp&#91;j - item.w] + item.v)\n<\/code><\/pre>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">3. The Constraint: Bounded Knapsack<\/h2>\n\n\n\n<p><strong>The Rule:<\/strong> Each item <math data-latex=\"i\"><semantics><mi>i<\/mi><annotation encoding=\"application\/x-tex\">i<\/annotation><\/semantics><\/math> has a specific limit <math data-latex=\"n_i\"><semantics><msub><mi>n<\/mi><mi>i<\/mi><\/msub><annotation encoding=\"application\/x-tex\">n_i<\/annotation><\/semantics><\/math>.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Approach A: Binary Splitting (The Clever Way)<\/h3>\n\n\n\n<p>A straightforward approach is to treat each item as <math data-latex=\"n_i\"><semantics><msub><mi>n<\/mi><mi>i<\/mi><\/msub><annotation encoding=\"application\/x-tex\">n_i<\/annotation><\/semantics><\/math> different items, each item has the same price and value. But it significantly slows down the algorithm.<\/p>\n\n\n\n<p>So instead of treating it as <math data-latex=\"n_i\"><semantics><msub><mi>n<\/mi><mi>i<\/mi><\/msub><annotation encoding=\"application\/x-tex\">n_i<\/annotation><\/semantics><\/math> individual items, we split <math data-latex=\"n_i\"><semantics><msub><mi>n<\/mi><mi>i<\/mi><\/msub><annotation encoding=\"application\/x-tex\">n_i<\/annotation><\/semantics><\/math> into powers of 2.<\/p>\n\n\n\n<p>For example, if you have 13 items, split them into packs of <strong>1, 2, 4, and <em>6<\/em><\/strong>. (Note that they should add up to <math data-latex=\"n_i\"><semantics><msub><mi>n<\/mi><mi>i<\/mi><\/msub><annotation encoding=\"application\/x-tex\">n_i<\/annotation><\/semantics><\/math>.) These four &#8220;new&#8221; items can combine to form any number from 1 to 13.<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Complexity:<\/strong> <math data-latex=\"O(W \\cdot \\sum \\log n_i)\"><semantics><mrow><mi>O<\/mi><mo form=\"prefix\" stretchy=\"false\">(<\/mo><mi>W<\/mi><mo>\u22c5<\/mo><mo movablelimits=\"false\">\u2211<\/mo><mrow><mi>log<\/mi><mo>\u2061<\/mo><mspace width=\"0.1667em\"><\/mspace><\/mrow><msub><mi>n<\/mi><mi>i<\/mi><\/msub><mo form=\"postfix\" stretchy=\"false\">)<\/mo><\/mrow><annotation encoding=\"application\/x-tex\">O(W \\cdot \\sum \\log n_i)<\/annotation><\/semantics><\/math><\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">Approach B: Monotonic Queue (The Ultimate Way)<\/h3>\n\n\n\n<p>For tight time limits, we use a <strong>Monotonic Queue<\/strong>. We group capacities by <math data-latex=\"j \\pmod{w_i}\"><semantics><mrow><mi>j<\/mi><mo><\/mo><mspace width=\"0.4444em\"><\/mspace><mo form=\"prefix\" stretchy=\"false\">(<\/mo><mrow><mtext><\/mtext><mi>mod<\/mi><\/mrow><mspace width=\"0.3333em\"><\/mspace><msub><mi>w<\/mi><mi>i<\/mi><\/msub><mo form=\"postfix\" stretchy=\"false\">)<\/mo><\/mrow><annotation encoding=\"application\/x-tex\">j \\pmod{w_i}<\/annotation><\/semantics><\/math>. For each group, we find the maximum value in a &#8220;sliding window&#8221; of size <math data-latex=\"n_i\"><semantics><msub><mi>n<\/mi><mi>i<\/mi><\/msub><annotation encoding=\"application\/x-tex\">n_i<\/annotation><\/semantics><\/math>.<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Complexity:<\/strong> <math data-latex=\"O(N \\cdot W)\"><semantics><mrow><mi>O<\/mi><mo form=\"prefix\" stretchy=\"false\">(<\/mo><mi>N<\/mi><mo>\u22c5<\/mo><mi>W<\/mi><mo form=\"postfix\" stretchy=\"false\">)<\/mo><\/mrow><annotation encoding=\"application\/x-tex\">O(N \\cdot W)<\/annotation><\/semantics><\/math> \u2014 the gold standard.<\/li>\n<\/ul>\n\n\n\n<details class=\"wp-block-details is-layout-flow wp-block-details-is-layout-flow\"><summary>Detailed implementation<\/summary>\n<h3 class=\"wp-block-heading\">1. The Core Observation<\/h3>\n\n\n\n<p>For an item with weight $w$, value $v$, and count $n$, notice that a capacity $j$ can only be updated by capacities like $j-w, j-2w, \\dots, j-kw$ (where $k \\le n$).<\/p>\n\n\n\n<p>This means the capacities are partitioned into <strong>disjoint sets<\/strong> based on their remainder when divided by $w$.<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Set 0:<\/strong> $0, w, 2w, 3w, \\dots$<\/li>\n\n\n\n<li><strong>Set 1:<\/strong> $1, w+1, 2w+1, 3w+1, \\dots$<\/li>\n\n\n\n<li><strong>Set $r$:<\/strong> $r, w+r, 2w+r, \\dots$<\/li>\n<\/ul>\n\n\n\n<p>The algorithm processes each remainder $r \\in [0, w-1]$ independently.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h3 class=\"wp-block-heading\">2. The Sliding Window Transformation<\/h3>\n\n\n\n<p>Let\u2019s look at the transition for a specific remainder $r$. Let $j = k \\cdot w + r$. The state looks like this:<\/p>\n\n\n\n<p>$$dp[k \\cdot w + r] = \\max_{k-n \\le i \\le k-1} \\{ dp[i \\cdot w + r] + (k &#8211; i) \\cdot v \\}$$<\/p>\n\n\n\n<p>To make this look like a standard &#8220;sliding window maximum&#8221; problem, we rearrange the terms:<\/p>\n\n\n\n<p>$$dp[k \\cdot w + r] = \\max_{k-n \\le i \\le k-1} \\{ dp[i \\cdot w + r] &#8211; i \\cdot v \\} + k \\cdot v$$<\/p>\n\n\n\n<p>Now, as $k$ increases, we are looking for the maximum value of $(dp[i \\cdot w + r] &#8211; i \\cdot v)$ in a window of size $n$.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h3 class=\"wp-block-heading\">3. Using the Monotonic Queue<\/h3>\n\n\n\n<p>A monotonic queue is a deque (double-ended queue) that keeps elements in <strong>decreasing order<\/strong>.<\/p>\n\n\n\n<ol start=\"1\" class=\"wp-block-list\">\n<li><strong>Maintain the Window:<\/strong> As we move to the next $k$, we remove indices from the front of the queue that are &#8220;out of date&#8221; (older than $k-n$).<\/li>\n\n\n\n<li><strong>Maintain Monotonicity:<\/strong> Before adding the new value $(dp[k \\cdot w + r] &#8211; k \\cdot v)$ to the back, we remove all values smaller than it. Why? Because a newer, larger value is always a better candidate for the future than an older, smaller value.<\/li>\n\n\n\n<li><strong>Get the Max:<\/strong> The current maximum for the window is always at the <strong>front<\/strong> of the queue.<\/li>\n<\/ol>\n<\/details>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">Summary Table<\/h2>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><thead><tr><td><strong>Problem Type<\/strong><\/td><td><strong>Core Idea<\/strong><\/td><td><strong>Best Complexity<\/strong><\/td><\/tr><\/thead><tbody><tr><td><strong>0\/1<\/strong><\/td><td>Each item used once<\/td><td>O(NW)<\/td><\/tr><tr><td><strong>Complete<\/strong><\/td><td>Infinite items, forward loop<\/td><td>O(NW)<\/td><\/tr><tr><td><strong>Bounded<\/strong><\/td><td>Finite count, Binary Splitting<\/td><td><math data-latex=\"O(NW \\log (\\max n_i))\"><semantics><mrow><mi>O<\/mi><mo form=\"prefix\" stretchy=\"false\">(<\/mo><mi>N<\/mi><mi>W<\/mi><mrow><mspace width=\"0.1667em\"><\/mspace><mi>log<\/mi><mo>\u2061<\/mo><\/mrow><mo form=\"prefix\" stretchy=\"false\">(<\/mo><mrow><mi>max<\/mi><mo>\u2061<\/mo><mspace width=\"0.1667em\"><\/mspace><\/mrow><msub><mi>n<\/mi><mi>i<\/mi><\/msub><mo form=\"postfix\" stretchy=\"false\">)<\/mo><mo form=\"postfix\" stretchy=\"false\">)<\/mo><\/mrow><annotation encoding=\"application\/x-tex\">O(NW \\log (\\max n_i))<\/annotation><\/semantics><\/math><\/td><\/tr><tr><td><strong>Bounded (Opt)<\/strong><\/td><td>Monotonic Queue<\/td><td>O(NW)<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">Final Thoughts<\/h2>\n\n\n\n<p>The Knapsack problem isn&#8217;t just about bags and gold; it&#8217;s about <strong>resource allocation<\/strong>. Whether you&#8217;re optimizing server load or a financial portfolio, these DP patterns are your best friends.<\/p>\n\n\n\n<p><\/p>\n","protected":false},"excerpt":{"rendered":"<p>The Knapsack problem is the &#8220;Hello World&#8221; of Dynamic Programming. But beyond the basic 0\/1 version lies a world of clever optimizations and mathematical tricks. In this post, we\u2019ll break down the three major types and the patterns that solve them. 1. The Foundation: 0\/1 Knapsack The Rule: You have one of each item. You [&hellip;]<\/p>\n","protected":false},"author":4,"featured_media":43,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[8],"tags":[],"class_list":["post-53","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-algorithm"],"_links":{"self":[{"href":"https:\/\/blog.mirpri.com\/index.php\/wp-json\/wp\/v2\/posts\/53","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/blog.mirpri.com\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/blog.mirpri.com\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/blog.mirpri.com\/index.php\/wp-json\/wp\/v2\/users\/4"}],"replies":[{"embeddable":true,"href":"https:\/\/blog.mirpri.com\/index.php\/wp-json\/wp\/v2\/comments?post=53"}],"version-history":[{"count":0,"href":"https:\/\/blog.mirpri.com\/index.php\/wp-json\/wp\/v2\/posts\/53\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/blog.mirpri.com\/index.php\/wp-json\/wp\/v2\/media\/43"}],"wp:attachment":[{"href":"https:\/\/blog.mirpri.com\/index.php\/wp-json\/wp\/v2\/media?parent=53"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blog.mirpri.com\/index.php\/wp-json\/wp\/v2\/categories?post=53"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blog.mirpri.com\/index.php\/wp-json\/wp\/v2\/tags?post=53"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}