P1272 [USACO02FEB] 重建道路

一道 提高+/省选− 的题, 原题链接, 这题主要是扫了一眼题解发现DP状态设计和代码实现都挺复杂, 于是打算自己写一篇, 第一次写写的不好请谅解;

一般来说看到这样的第一反应就是 dp[i, j] 表示以i为跟的子树中, 保留j个节点最少需要切掉的边数, 可是玩了一会发现这样转移其实并不好转移, 所以考虑正难则反, 我们用 dp[u, j] 表示以u为根节点, 切掉j个节点最少需要切掉的边;

这样我们的转移一下子就方便很多了: dp[u, i] = min(dp[u, i], dp[v, j] + dp[u, i - j]), 其中v是u的子节点, 但是这样子会漏考虑直接把v全部切掉的情况, 所以可以直接令dp[v, sz[v]] = 1, 表示直接把v切掉所需要的边数;

于是就有了一份很简洁的代码:

inline void solve() {
    int n, m;
    cin >> n >> m;

    vvi g(n + 1);
    for (int i = 1; i < n; i++) {
        int u, v;
        cin >> u >> v;
        g[u].push_back(v);
        g[v].push_back(u);
    }

    vvi dp(n + 1, vi(n + 1, inf));
    vi sz(n + 1);
    auto dfs = [&](auto&& dfs, int u, int fa) -> void {
        sz[u] = 1, dp[u][0] = 0;
        for (int v : g[u]) {
            if (v == fa) continue;
            dfs(dfs, v, u);
            sz[u] += sz[v];
            for (int i = sz[u]; i >= 0; i--) {
                for (int j = 0; j <= min(i, sz[v]); j++) {
                    dp[u][i] = min(dp[u][i], dp[v][j] + dp[u][i - j]);
                }
            }
        }
        dp[u][sz[u]] = 1;
    };
    dfs(dfs, 1, 0);

    int ans = dp[1][n - m];
    for (int i = 2; i <= n; i++) {
        if (sz[i] - m >= 0) ans = min(ans, dp[i][sz[i] - m] + 1);
    }
    cout << ans << endl;
}

posted @ 2026-06-06 08:31  RCells  阅读(3)  评论(0)    收藏  举报