【树形DP】POJ 1947 Rebuilding Roads
通道:http://poj.org/problem?id=1947
题意:给出n,p,一共有n个节点,要求减去最少的边是多少,剩下p个节点
思路: dp[u][i]:记录u结点,要得到一棵i个节点的子树去掉的最少边数
考虑其儿子k
1)如果不去掉k子树,则 dp[u][i] = min(dp[u][j]+dp[k][i-j]) 0 <= j <= i
2)如果去掉k子树,则 dp[u][i] = dp[u][i]+1 ,所以最后为: dp[u][i] = min (min(dp[u][j]+dp[k][i-j]) , dp[u][i]+1 );
答案最后就是在dp[i][m]中取小,要注意的一点是,如果i不是根,值还需要+1,因为要脱离原来的根,还要去掉一条边
代码:
#include <cstdio> #include <cstring> #include <algorithm> using namespace std; int const INF = 0x3fffffff; int const MAX = 155; struct Edge { int to, next; }e[MAX * MAX / 2]; int n, m; int head[MAX], cnt, root; int dp[MAX][MAX]; void Add(int x, int y) { e[cnt].to = y; e[cnt].next = head[x]; head[x] = cnt++; } void DFS(int u, int fa) { for(int i = 0; i <= m; i++) dp[u][i] = INF; dp[u][1] = 0; for(int i = head[u]; i != -1; i = e[i].next) { int v = e[i].to; if (v == fa) continue; DFS(v, u); for(int j = m; j >= 1; j--) { for(int k = 0; k < j; k++) { if(k) dp[u][j] = min(dp[u][j], dp[u][j - k] + dp[v][k]); else dp[u][j] = dp[u][j] + 1; } } } } int main() { scanf("%d %d", &n, &m); cnt = 0; memset(head, -1, sizeof(head)); for(int i = 1; i < n; i++) { int x, y; scanf("%d %d", &x, &y); Add(x, y); Add(y, x); } DFS(1, -1); int ans = dp[1][m]; for(int i = 1; i <= n; i++) ans = min(ans, dp[i][m] + 1); printf("%d\n",ans); }