Time Limit: 1000MS | Memory Limit: 30000K | |
Total Submissions: 9499 | Accepted: 4317 |
Description
Input
Output
Sample Input
11 6
1 2
1 3
1 4
1 5
2 6
2 7
2 8
4 9
4 10
4 11
Sample Output
2
Hint
Source
题目链接:http://poj.org/problem?id=1947
题目大意:一颗含有n个结点的树,求减去最少的边使得该树只含有p个结点
题目分析:典型的树形dp,在树上进行动态规划,设dp[s][i]表示以s为根的子树含有i个结点需要删去的边数,则得到转移方程:
对于i的子树k:
1.不加子树k,dp[s][i] = dp[s][i] + 1 (不加子树k,相当于删去一条边)
2.加子树k,dp[s][i] = min(dp[s][j] + dp[k][i - j]) (1<= j <= i) (以s为根的树的结点数加上其子树的结点数等于i)
最后答案就是在dp[i][m]中取小,要注意的一点是,如果i不是根,值还需要+1,因为要脱离原来的根,还要去掉一条边
这里需要注意,dp过程是自下而上的,也就是从叶子到根,而不是从根到叶子
#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 fa[MAX], 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) { 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; //u的子树 DFS(v); for(int j = m; j >= 1; j--) { for(int k = 0; k < j; k++) { if(k) //不加子树k dp[u][j] = min(dp[u][j], dp[u][j - k] + dp[v][k]); else //加上子树k dp[u][j] = dp[u][j] + 1; } } } } int main() { scanf("%d %d", &n, &m); cnt = 0; memset(fa, -1, sizeof(fa)); memset(head, -1, sizeof(head)); for(int i = 1; i < n; i++) { int x, y; scanf("%d %d", &x, &y); Add(x, y); fa[y] = x; } for(root = 1; fa[root] != -1; root = fa[root]); DFS(root); int ans = dp[root][m]; for(int i = 1; i <= n; i++) //除了根节点,其他节点要想成为独立的根,必先与父节点断绝关系,所以要先加1 ans = min(ans, dp[i][m] + 1); printf("%d\n",ans); }
POJ 1947 Rebuilding Roads (树形dp 经典题)
原文地址:http://blog.csdn.net/tc_to_top/article/details/44134065