NeFut Logo NeFut
Admin Login

Tree-dependent Knapsack Problem: The Perfect Integration of Topological Dependencies and Dynamic Programming

Published at: 2026-05-27 09:17 Last updated: 2026-06-06 13:04
#algorithm #Dynamic Programming #Tree

Core Logic and Mathematical Principles

The Tree-dependent Knapsack, also known as the Knapsack Problem with Dependencies, is the most powerful classic advanced model in tree dynamic programming. Its essence lies in the high-dimensional fusion of topological tree dependencies from graph theory and the state network of grouped knapsack from combinatorial mathematics.

1. Geometric Dimensionality Reduction of Tree Dependencies

In the linear knapsack problem, various items are parallel and independent. However, in the tree-dependent knapsack, if a node $u$ is selected for the knapsack, all its child nodes $v \in \text{son}(u)$ are eligible to be included in the knapsack; conversely, if the parent node $u$ is not selected, its entire subtree will be forcibly "pruned".

This complex topological dependency can be locally decoupled through the post-order traversal of DFS:

For any parent node $u$, given that it is selected, each of its direct child nodes $v$ can be regarded as an independent group. Allocating different volumes $k$ to subtree $v$ will yield different maximum value returns. This is essentially a group knapsack problem.

2. Physical Significance of Polynomial Multiplication and State Convolution

Let the total volume allocated by parent node $u$ to its subtree $v$ be $k$. Then the maximum value that various combinations of items within subtree $v$ can contribute under the volume constraint of $k$ is a complete, solidified local optimal state. The process of parent node $u$ allocating total capacity $j$ to its various subtrees is mathematically equivalent to state convolution of multiple polynomials:

$$f[u][j] = \max \left( f[u][j], f[u][j - k] + f[v][k] \right)$$

where $j$ loops in reverse order to ensure that the state of the current subtree $v$ is updated using the unpolluted predecessor state of the parent node (strictly adhering to the mutual exclusion control of the grouped knapsack).


State Design and Algorithm Derivation

1. State Space Definition

Let $f[u][j]$ represent the maximum value obtainable when the total volume (or total funds spent) does not exceed $j$ in the subtree rooted at $u$.

2. Derivation of State Transition Equation

For node $u$, let its own volume be $w[u]$ and value be $v[u]$. The total knapsack capacity is $V$.

  1. Basic Element State Initialization:

$$f[u][j] = v[u] \quad (\forall w[u] \le j \le V)$$

This indicates the profit from selecting only the root node $u$ without allocating any capacity to its subtrees. 2. Three-layer Loop Convolution Transition: Iterate through each child node $v$ of $u$:

$$f[u][j] = \max_{w[u] \le j \le V} \left\{ \max_{0 \le k \le j - w[u]} \{ f[u][j - k] + f[v][k] \} \right\}$$


C++ Standard Code (NOIP Style)

The following code is a standard implementation of the tree-dependent knapsack, using a high-performance undirected graph construction method with safe sentinel interception, perfectly compatible with the Linux g++ -O2 environment.

#include <iostream>
#include <vector>
#include <algorithm>
#include <cstring>

using namespace std;

const int MAXN = 305;
const int MAXV = 305;

int n, m; // n is the total number of nodes, m is the total capacity of the knapsack
int w[MAXN], v[MAXN];
int f[MAXN][MAXV];
vector<int> edge[MAXN];

// Core of the tree-dependent knapsack: utilizing DFS to dynamically intertwine knapsack states
void dfs(int u, int fa) {
    // 1. Forced initialization: once the subtree rooted at u is selected, u must occupy w[u] capacity
    for (int j = w[u]; j <= m; ++j) {
        f[u][j] = v[u];
    }

    // 2. Enumerate child nodes ("groups" in the grouped knapsack)
    for (int v_node : edge[u]) {
        if (v_node == fa) continue; // Avoid reverse loop in undirected edges

        dfs(v_node, u); // Topological sinking, allow child nodes to flush their knapsack network first

        // 3. Enumerate parent node knapsack capacity in reverse order (classic capacity axis of grouped knapsack)
        // Critical pitfall: must be in reverse order, and the lower bound is w[u], leaving space for the root node itself
        for (int j = m; j >= w[u]; --j) {
            // 4. Enumerate the capacity k allocated to the current subtree v_node ("items within the group")
            for (int k = 0; k <= j - w[u]; ++k) {
                f[u][j] = max(f[u][j], f[u][j - k] + f[v_node][k]);
            }
        }
    }
}

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

    if (!(cin >> n >> m)) return 0;

    // Exam tip: Since there may be multiple independent trees (forest structure),
    // create a virtual "0th super root node" with w[0]=0, v[0]=0, unifying the forest into a single tree rooted at 0
    w[0] = 0; v[0] = 0;

    for (int i = 1; i <= n; ++i) {
        int parent;
        cin >> parent >> w[i] >> v[i];

        // Establish bidirectional (or unidirectional) topological connections
        edge[parent].push_back(i);
        edge[i].push_back(parent);
    }

    // Start DFS from the virtual root node 0
    dfs(0, -1);

    // The final answer is the maximum value obtainable with total capacity m at the virtual root node
    cout << f[0][m] << "\n";

    return 0;
}

NOIP Practical Pitfall Guide

1. Confusion in the Order of Three-layer Loop Control Leading to State Pollution

2. Inner Loop Boundary Fails to Reserve Space for Parent Node


Classic NOIP/Luogu Problems

1. Luogu P2014 [CTSC1997] Course Selection

2. Luogu P1273 Cable Television Network

$$f[u][j] = \max \left( f[u][j], f[u][j - k] + f[v][k] - \text{cost}(u, v) \right)$$

During the DFS backtracking, the parent node updates the table by combining the number of users from the subtrees $k$. Finally, at the state axis of the root node $root$, search from large to small for the first $j$ that satisfies $f[root][j] \ge 0$. This $j$ represents the maximum number of users that can be retained. This problem perfectly demonstrates the high-level technique of "interchanging dimensions of volume and value" in the knapsack problem.

Original Source: local://16.2

[h] Back to Home