POJ 1947 Rebuilding Roads【树状DP】


POJ 1947 Rebuilding Roads

http://poj.org/problem?id=1947
大意:有n个点组成一个树,问至少要删除多少条边才能获得一棵有p个结点的子树?
分析:树形DP
    满足上诉条件的子树必由某个结点和它的若干子树组成,
 1.用dp[i][j]表示结点i恰好保留j个子树时至少需要删除的边数,若结点i有子树s
   a.删除子树s,dp[i][j]+1
   b.子树s保留k个结点:dp[s][k-1]+dp[i][j-k]
   综上:dp[i][j] =min(dp[i][j]+1,dp[s][k-1]+dp[i][j-k])(0<k<=j)
  
 2.默认结点1为根DFS,获取dp[i][j]值
    3.遍历所有的节点
    a.若子树根为结点1,则需删除的边为dp[1][p-1]
    b.若子树的根为节点i(i!=1),则需删除的边为dp[i][p-1]+1;
   综上:ans  = min(dp[i][p-1]+1,dp[1][p-1])(1<i<=n)

View Code
1 #include < stdio.h >
2 #include < string .h >
3 const int N = 150 + 10 ;
4 struct node
5 {
6 int v;
7 node * next;
8 }edge[N * 2 ], * index[N];
9 int ednum;
10 bool visited[N];
11
12 inline void addEdge( int a, int b)
13 {
14 node * p = & edge[ednum ++ ];
15 p -> v = b;
16 p -> next = index[a];
17 index[a] = p;
18 }
19
20 int dp[N][N]; // dp[i][j]表示子树i保留j个后代至少需要删除多少边
21 int n,p; // n为原树节点个数,p为所求子树的节点数
22
23 inline int min( int a, int b)
24 {
25 return a < b ? a:b;
26 }
27
28 int dfs( int id) // 求子树id的dp值,并返回原树中以id为根的子树包含的结点数目
29 {
30 if (visited[id] == true ) return 0 ;
31 visited[id] = true ;
32
33 int sonNum = 0 ,i,j; // sonNum统计当前节点的后代数目
34
35 for (i = 1 ;i <= n;i ++ ) // 初始化,dp[id][i] = n表示该状态不可达
36 dp[id][i] = n;
37
38 dp[id][ 0 ] = 0 ; // dp[id][0]表示未访问子结点前,即默认没有子节点的情况下0个孩子需要的删除0条边即可
39
40 for (node * p = index[id];p;p = p -> next)
41 {
42 if (visited[p -> v] == false )
43 {
44 int curSon = dfs(p -> v);
45 sonNum += curSon;
46
47 for (i = sonNum;i > 0 ;i -- ) // 转化为01背包求解
48 {
49 int upper = curSon;
50 if (upper > i)upper = i;
51 int temp = dp[id][i] + 1 ;
52 for (j = 1 ;j <= upper;j ++ )
53 {
54 temp = min(temp,dp[p -> v][j - 1 ] + dp[id][i - j]);
55 }
56 dp[id][i] = temp;
57 }
58 dp[id][ 0 ] ++ ;
59 }
60 }
61
62 sonNum ++ ; // 计入本身
63 return sonNum;
64 }
65
66 int main()
67 {
68 while (scanf( " %d%d " , & n, & p) != EOF)
69 {
70 int i;
71 for (i = 1 ;i <= n;i ++ )
72 {
73 index[i] = NULL;
74 visited[i] = false ;
75 }
76
77 int a,b;
78 ednum = 0 ;
79 for (i = 1 ;i < n;i ++ )
80 {
81 scanf( " %d%d " , & a, & b);
82 addEdge(a,b);
83 addEdge(b,a);
84 }
85
86 dfs( 1 ); // 计算dp值
87 int ans = dp[ 1 ][p - 1 ];
88 for (i = 2 ;i <= n;i ++ )
89 {
90 if (dp[i][p - 1 ] + 1 < ans)ans = dp[i][p - 1 ] + 1 ;
91 }
92
93 printf( " %d\n " ,ans);
94 }
95 return 0 ;
96 }

转载于:https://www.cnblogs.com/AndreMouche/archive/2011/03/26/1996102.html

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值