题目链接:http://poj.org/problem?id=1947
题目大意:给定一棵节点数为n的树,问从这棵树最少删除几条边使得某棵子树的节点个数为p,1<=n<=150,1<=p<=n。
解题思路:树形DP + 背包。由于给定的结构是树,就要想到树的递归特性,而树形dp的优美之处是可以利用子树的状态来转移,来求得根的状态。本题要求求最少删除几条边使得子树节点个数为p,我们只要算出每个以节点i为根的树中节点个数为p的最少删除边数,求个最小值就好。其实我们可以这样想,每棵以i为根的树有sum种物品(sum为他以及与他的子孙节点的个数),必须要删除k条边才能使得这棵子树有j个节点(1<=j<=sum),那么每个物品j的费用是k,价值是j,这样问题就转换为在树上的分组背包,总共有n组物品,每次都从以i为根的物品组中选择一个物品进行转移,每组选择一个物品。由于根节点固定了是1,我把树看成有向的树,也就是每次求解都不管父节点,很多人的解题报告里都有管父亲节点,但我觉得那样不好理解。
现在设dp[i][j]表示以i为根的子树中节点个数为j的最少删除边数(从分组背包角度理解就是到转移到第i组价值为j的最少费用)
状态转移方程: dp[i][1] = tot (tot为他的子节点个数)
dp[i][j] = min(dp[i][j],dp[i][k]-1+dp[s][j-k]) (1<=i<=n,2<=j<=sum(节点总和),1<=k<j,s为i子节点)(i中已有k个节点并从s中选择j-k个,算最少删除边数,s选上所以i->s的边不需删除,所以-1)
测试数据:
2 1
代码:
#include <stdio.h> #include <string.h> #define MAX 500 #define INF 1000000000 #define min(a,b) (a)<(b)?(a):(b) struct node { int v; node *next; }*head[MAX],tree[MAX]; int n,ans,dp[MAX][MAX]; int m,ptr,sum[MAX],vis[MAX]; void AddEdge(int a,int b) { tree[ptr].v = b; tree[ptr].next = head[a]; head[a] = &tree[ptr++]; tree[ptr].v = a; tree[ptr].next = head[b]; head[b] = &tree[ptr++]; } void Solve_1A(int in) { if (vis[in]) return; int i,j,k,son,pa,tot; tot = 0; vis[in] = sum[in] = 1; node *p = head[in]; while (p != NULL) { //获取他的子树数量tot,和子节点数量sum[in] if (!vis[p->v]) { //先出现过的为父节点 Solve_1A(p->v); sum[in] += sum[p->v]; tot++; } p = p->next; } p = head[in]; //if (in != 1) tot++; //除了根节点,其他点的都有父节点,要把与父节点相连的边也删去 dp[in][1] = tot; while (p != NULL) { int v = p->v; //子节点编号 for (j = sum[in] + 1; j >= 2; --j) for (k = 1; k < j; ++k) if (dp[in][k] != INF && dp[v][j-k] != INF) dp[in][j] = min(dp[in][j],dp[in][k]-1+dp[v][j-k]); p = p->next; } } int main() { int i,j,k,t; while (scanf("%d%d",&n,&m) != EOF) { ptr = 1,ans = INF; memset(vis,0,sizeof(vis)); memset(head,NULL,sizeof(head)); for (i = 0; i <= n; ++i) for (j = 0; j <= n; ++j) dp[i][j] = INF; for (i = 1; i < n; ++i) { int a,b; scanf("%d%d",&a,&b); AddEdge(a,b); } Solve_1A(1); for (i = 1; i <= n; ++i) { if (i == 1) ans = min(ans,dp[i][m]); else ans = min(ans,dp[i][m]+1);//非根节点要删除连到父亲节点的那条边 } printf("%d\n",ans); } }
本文ZeroClock原创,但可以转载,因为我们是兄弟。