NeFut Logo NeFut
Admin Login

Tree Dynamic Programming: Analyzing the Bossless Ball and the Maximum Independent Set Problem

Published at: 2026-05-27 09:17 Last updated: 2026-06-06 13:04
#C++ #Tutorial

Core Logic and Mathematical Principles

Tree Dynamic Programming (Tree DP) is an algorithmic model that establishes the progression of dynamic programming stages on a tree topology (a directed acyclic graph). Its essence lies in the natural topological order of post-order traversal (DFS), enabling the linear energy transfer of states from child nodes to parent nodes.

1. Geometric Specialization of Topological Order

In linear DP, stages progress orderly along a one-dimensional axis (as indicated by the subscript $i$). In a tree structure, the optimal decision of a parent node $u$ depends on the fixed states of all its child nodes $v \\in ext{son}(u)$. Since trees are acyclic, we can collect child node information through a single Depth-First Search (DFS) during the recursive backtrack (i.e., at the post-order traversal position). At this point, the lifecycle of the child nodes has ended, and their states are optimal, perfectly satisfying the no post-order property.

2. The Bossless Ball (Classic Maximum Independent Set Model)

Given a tree with parent-child relationships, each node has a happiness value. We need to select a subset of nodes such that no two connected nodes (i.e., parent-child relationships) can be selected simultaneously. The goal is to find the maximum sum of happiness values of this subset.


State Design and Algorithm Derivation

1. State Space Definition

Let $f[u][0]$ represent the maximum happiness value obtainable when not selecting node $u$ in the subtree rooted at $u$. Let $f[u][1]$ represent the maximum happiness value obtainable when selecting node $u$ in the subtree rooted at $u$.

2. Derivation of State Transition Equations

For node $u$, let its child node set be $ ext{son}(u)$, and the original happiness value of the current node be $R[u]$:

$$f[u][1] = R[u] + \sum_{v \\in \text{son}(u)} f[v][0]$$

$$f[u][0] = \sum_{v \\in \text{son}(u)} \max(f[v][0], f[v][1])$$

3. Boundary Conditions

When $u$ is a leaf node ($ ext{son}(u) = \emptyset$):

$$f[u][0] = 0, \quad f[u][1] = R[u]$$

This boundary condition is naturally activated when the DFS backtrack reaches the leaf nodes.


C++ Standard Source Code (NOIP Style)

The following code is the standard implementation for the "Bossless Ball" exam case. It uses a high-performance adjacency list (std::vector) to construct the tree, strictly adhering to the Linux g++ -O2 compilation standard.

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

using namespace std;

const int MAXN = 6005;

int r[MAXN];          // Store the happiness value of each node
int f[MAXN][2];       // f[u][0]: do not select u, f[u][1]: select u
bool has_parent[MAXN];// Mark whether there is a parent, used to find the root of the tree
vector<int> tree[MAXN];// Adjacency list to build the tree

// Core of Tree DP: Use DFS backtracking to achieve bottom-up state transitions
void dfs(int u) {
    // 1. Initialize the basic state of leaf nodes
    f[u][0] = 0;
    f[u][1] = r[u];

    // 2. Propagate down the topology
    for (int v : tree[u]) {
        dfs(v); // First, let child nodes calculate their own subtree states

        // 3. Update the parent node using child node states during backtracking (contribution from bottom to top)
        f[u][1] += f[v][0];                       // If u is selected, child node v cannot be selected
        f[u][0] += max(f[v][0], f[v][1]);         // If u is not selected, child node v can choose the maximum
    }
}

int main() {
    // Optimize I/O performance
    ios_base::sync_with_stdio(false);
    cin.tie(NULL);

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

    for (int i = 1; i <= n; ++i) {
        cin >> r[i];
    }

    // Read relationships and build directed tree (from parent to child)
    for (int i = 1; i < n; ++i) {
        int l, k; // l is a direct subordinate of k (l is child, k is parent)
        cin >> l >> k;
        tree[k].push_back(l);
        has_parent[l] = true; // Mark that l has a parent
    }

    // Find the geometric root of the entire tree (the node without a parent is the root)
    int root = 1;
    while (has_parent[root]) {
        root++;
    }

    // Start the DFS state network intertwining from the root node
    dfs(root);

    // The global optimal solution must arise from the two terminal states of "selecting root" and "not selecting root"
    cout << max(f[root][0], f[root][1]) << "\n";

    return 0;
}

NOIP Practical Pitfall Guide

1. Undirected Graph Tree Construction Leading to Infinite Loop and Stack Overflow

2. Incorrect Root Node Localization


Classic NOIP/Luogu Problems

1. Luogu P1352 Bossless Ball

2. Luogu P2015 Binary Apple Tree

$$f[u][j] = \max_{v \\in \text{son}(u)} \{ f[u][j - k] + f[v][k - 1] + \text{apple}(u, v) \}$$

This model perfectly integrates tree structure with dynamic programming capacity knapsack.

Original Source: local://16.1

[h] Back to Home