Rebuilding Roads
分类:
dfs and similar
trees
dp
1.题意概述
- 给你一颗有
n
个节点构成是树,现在要你删去最少的边,使得剩下连通的点构成的树刚好有
p 个节点。
2.解题思路
- 容易想到是一个树形dp,
dp[i][j]
表示以
i
节点为根节点,得到
j 个节点的子树需要去掉的最少边数,那么转移方程为:考虑其儿子 k 选或者不选——最后取个最小值就行
- 如果不去掉
k 的子树: dp[s][i]=min(dp[s][j]+dp[k][i−j]),0≤j≤i - 如果去掉
k
的子树:
dp[s][i]=dp[s][i]+1
3.AC代码
#define N 152 int f[N][N], sonA[N], sonB[N]; bool pa[N]; int n, p; void init() { memset(pa, 0, sizeof pa); memset(sonA, 0, sizeof sonA); memset(sonB, 0, sizeof sonB); } void dfs(int u) { fill(f[u], f[u] + p + 1, INF); f[u][1] = 0; int k = sonA[u]; while (k) { dfs(k); per(i, 1, p + 1) { int tmp = f[u][i] + 1; rep(j, 1, i) tmp = min(tmp, f[k][i - j] + f[u][j]); f[u][i] = tmp; } k = sonB[k]; } // rep(i, 1, p + 1) printf("%d ", f[u][i]); // puts(""); } int main() { while (~scanf("%d%d", &n, &p)) { init(); rep(i, 1, n) { int u, v; scanf("%d%d", &u, &v); pa[v] = 1; sonB[v] = sonA[u]; sonA[u] = v; } int sta = 0; rep(i, 1, n + 1) if (!pa[i]) { sta = i; break; } dfs(sta); int ans = f[sta][p]; rep(i, 1, n + 1) { if (i != sta) ans = min(ans, f[i][p] + 1); } printf("%d\n", ans); } return 0; }
- 如果不去掉