AcWing 10. 有依赖的背包问题
考察:树形dp
思路:
树形背包的时间复杂度是O(n3)
按划分给子节点的体积来分配集合.思路与苹果树大体相同.关于几个问题需要解释下:
- 为什么不和苹果树那题一样在遍历点的时候+w[u]. 答: k不一定能装下,会使得背包里的价值多了.除此之外,f[u][j] = f[u][j-k]+f[vs][k],f[u][j-k]已经计入了w[u],在for循环里加会重复.
- 为什么要遍历完后特地加for(int i=m;i>=v[u];i--) f[u][i] = f[u][i-v[u]]+w[u]; 答:加上原本的父结点
- 为什么上面的循环是赋值而不是max. 答:dfs循环里可能已经赋值了,但是我们不是必须放子节点,而是必须放根节点.
- 遍历点外的for循环是否能是正序? 答:不能,这是01背包,需要逆序推,否则f[i-v[u]]是已经被更新过的了.
- 内层循环为什么从大到小遍历:因为要用上一个子节点的最优值.
1 #include <iostream> 2 #include <cstring> 3 #include <algorithm> 4 #include <cstdio> 5 using namespace std; 6 const int N = 110; 7 int v[N],w[N],n,m,root,idx,h[N]; 8 int f[N][N]; 9 struct Road{ 10 int fr,to,ne; 11 }road[N]; 12 void add(int a,int b) 13 { 14 road[idx].fr = a,road[idx].to = b,road[idx].ne = h[a],h[a] = idx++; 15 } 16 void dfs(int u) 17 { 18 for(int i=h[u];i!=-1;i=road[i].ne) 19 { 20 int vs = road[i].to; 21 dfs(vs); 22 for(int j=m-v[u];j>=0;j--)//给子树的空间不得多于减去放根节点的空间 23 for(int k=0;k<=j;k++) 24 f[u][j] = max(f[u][j],f[vs][k]+f[u][j-k]);//为什么不在这里加子节点体积,因为k可能放不下. 25 } 26 for(int i=m;i>=v[u];i--) f[u][i] = f[u][i-v[u]]+w[u]; 27 for(int i=0;i<v[u];i++) f[u][i] = 0; 28 } 29 int main() 30 { 31 memset(h,-1,sizeof h); 32 scanf("%d%d",&n,&m); 33 for(int i=1;i<=n;i++) 34 { 35 int p; 36 scanf("%d%d%d",&v[i],&w[i],&p); 37 if(p==-1) root = i; 38 else add(p,i); 39 } 40 dfs(root); 41 printf("%d\n",f[root][m]); 42 return 0; 43 }