题解:P12136 [蓝桥杯 2025 省 B] 生产车间

· · 题解

组合问题,观察到数据范围 1000,考虑树形 DP。因为每个节点的可能提供的值不确定,所以还需要利用分组背包来实现。

思路分析

状态定义

定义 dp[u] 为 bitset 类型。例如 dp[u][x] = true 表示在以节点 u 为根的子树中,通过删除部分节点,可以使得子树提供恰好 x 的价值。

状态转移

采用树形 DP 的经典形式,DFS,从叶子节点开始,自底向上。

时间复杂度

具体实现详细见代码:

const int N = 1e3+9;
int n, ans=0;
vector<int> w(N);
vector<vector<int>> g(N);
vector<bitset<N>> dp(N);

void dfs(int u, int p) {
    if (g[u].size() == 1 && p != -1) {  // 叶子节点判断
        dp[u][0] = 1;
        if (w[u] <= N) dp[u][w[u]] = 1;
        return;
    }

    bitset<N> cur, mask; cur[0] = 1;
    for (int i = 0; i <= w[u]; ++i) mask.set(i);

    for (int v : g[u]) if (v != p) {
        dfs(v, u);
        bitset<N> nxt; nxt[0] = 1;
        for (int s = cur._Find_first(); s <= w[u]; s = cur._Find_next(s)) nxt |= (dp[v] << s);
        nxt &= mask;
        cur = nxt;
    }
    dp[u] = cur;
}

signed main () {
    cin >> n;   // 输入
    for (int i = 1; i <= n; i++) cin >> w[i];   
    for (int i = 1; i < n; i++) {
        int u, v; cin >> u >> v;
        g[u].push_back(v); g[v].push_back(u);
    }

    dfs(1, -1); // 树形 DP

    for (int x = w[1]; x >= 0; x--) if (dp[1][x] == 1) {ans = x; break;}    // 输出
    cout << ans << '\n';
}

值得一提的是,这可能是一道假题。因为题目并没有提到“若将 u 的所有子节点都删除后,u 不会因为转变为叶子节点而转变为产出材料”。