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 }