void-man

  博客园 :: 首页 :: 博问 :: 闪存 :: 新随笔 :: 联系 :: 订阅 订阅 :: 管理 ::

树形dp的第一道题,本来感觉dp类题目就够难了,dp方程不好找,没想到又来个树形dp,好吧,看了答案姑且看懂了,自己编了下a了

树形dp有几点需要注意:

1 找到状态转移方程

2 考虑好边界条件

这道题目的意思就是给你一棵树,问你至少删除几条边,可以得到一个p个节点构成的子树...

首先用vector模拟二维数组存储边的结构,然后dp[root][p]表示以root为根的树得到一个p节点的子树需要删除的最少边数

初始化时候都为INF,但是需要注意点初始化dp[root][1]=0,因为在每次遍历root的孩子时候如果不删除,那么至少要保留一个节点root本身

所以可以得到,v为root的一个孩子

1 如果切,则dp[root][p]=dp[root][p]+1

2 如果不切,那我们就可以保留v为根的子树的某些节点(起码保留v,因为不切嘛~),这时我们就可以枚举子树保留k个节点,而在整树在找p-k个节点,这些也是子问题。

1 #include <stdio.h>
2 #include <string.h>
3 #include <vector>
4  #define N 200
5  #define inf 1e8
6  using namespace std;
7 int n,p,dp[N][N];
8 vector<int>V[N];
9 inline int MIN(int a,int b)
10 {
11 return a>b?b:a;
12 }
13 void init()
14 {
15 for(int i=0;i<N;i++)
16 V[i].clear();
17 //memset(dp,0,sizeof(dp));
18 }
19 void dfs(int root)
20 {
21 for(int i=0;i<=p;i++)
22 dp[root][i]=inf;
23 dp[root][1]=0;
24 for(int i=0;i<V[root].size();i++)
25 {
26 int son=V[root][i];
27 dfs(son);
28 for(int j=p;j>0;j--)
29 {
30 int tmp=inf;
31 for(int k=1;k<j;k++)
32 {
33 tmp=MIN(tmp,dp[root][k]+dp[son][j-k]);//当前son不与父亲节点剪掉
34 }
35 dp[root][j]=MIN(dp[root][j]+1,tmp);//比较剪掉与不剪掉的最小值,剪掉的话dp[root][j]+1
36 }
37 }
38
39 }
40 int main()
41 {
42 int pa,so,ans;
43 while(scanf("%d%d",&n,&p)!=EOF)
44 {
45 init();
46 for(int i=0;i<n-1;i++)
47 {
48 scanf("%d%d",&pa,&so);
49 V[pa].push_back(so);
50
51 }
52 dfs(1);
53 ans=dp[1][p];
54 for(int i=2;i<=n;i++)
55 {
56 if(dp[i][p]+1<ans)
57 ans=dp[i][p]+1;
58 }
59 printf("%d\n",ans);
60 }
61
62 }
posted on 2011-06-02 20:45  void-man  阅读(175)  评论(0)    收藏  举报