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;
}

浙公网安备 33010602011771号