HDU1561 The more, The Better(树形DP)

题目是有n个存有宝藏的城堡,攻克任何一个城堡都需要先攻克0个或其他1个城堡,问攻克m个城堡最多能得到多少宝藏。

题目给的城堡形成一个森林,添加一个超级根把森林连在一起就是树了,那么就考虑用树型DP:

  • dp[u][m]表示以u结点为根的子树攻克m个结点的最大价值

但是这样转移太难了,根是从每个孩子通过各自分配若干的城堡去攻克转移的,一个排列组合数,阶乘,是指数级的时间复杂度!

看了题解,原来这是依赖背包,没看背包九讲。。不过网上的博客似乎没说清楚,事实上这个状态应该是三个维度来表示:

  • dp[u][m][n]表示以u结点为根的子树,且只考虑u结点的前n个孩子,去攻克m个结点,所能得到的最大价值
  • 转移就是dp[u][m][n]=max(dp[u][m-k][n-1]+dp[u的第n个孩子][k][u的孩子个数])(0<=k<m)

可以发现n只从n-1转移而来,那么就可以像01背包重复利用内存,这三维数组只用二维数组来实现,即:

  • dp[u][m]=max(dp[u][m-k]+dp[u’][k])(father[u']=u,0<=k<m)
  • m要从大到小枚举。

代码感觉还不好实现。。感觉这题挺难的= =虽然听说这是树型DP入门题。。

 1 #include<cstdio>
 2 #include<cstring>
 3 #include<algorithm>
 4 using namespace std;
 5 struct Edge{
 6     int u,v,next;
 7 }edge[222];
 8 int NE,head[222];
 9 void addEdge(int u,int v){
10     edge[NE].u=u; edge[NE].v=v; edge[NE].next=head[u];
11     head[u]=NE++;
12 }
13 int val[222],d[222][222];
14 void dp(int u,int m){
15     d[u][1]=val[u];
16     for(int i=head[u]; i!=-1; i=edge[i].next){
17         int v=edge[i].v;
18         dp(v,m);
19         for(int j=m; j>=2; --j){
20             for(int k=0; k<j; ++k){
21                 d[u][j]=max(d[u][j],d[u][j-k]+d[v][k]);
22             }
23         }
24     }
25 }
26 int main(){
27     int n,m,a;
28     while(~scanf("%d%d",&n,&m) && (n||m)){
29         NE=0;
30         memset(head,-1,sizeof(head));
31         for(int i=1; i<=n; ++i){
32             scanf("%d%d",&a,val+i);
33             addEdge(a,i);
34         }
35         memset(d,0,sizeof(d));
36         dp(0,m+1);
37         printf("%d\n",d[0][m+1]);
38     }
39     return 0;
40 }

 

posted @ 2016-02-12 19:15  WABoss  阅读(953)  评论(1编辑  收藏  举报