个人技术分享

算法提高之有依赖的背包问题

  • 核心思想:分组背包 + 树形dp

    • 因为不取父节点,子节点取不了 所以dfs递归从子节点开始,向上返回
    • f[i][j]表示以i为根节点总体积不超过j的方案
  •   #include <iostream>
      #include <cstring>
      #include <algorithm>
      
      using namespace std;
      const int N = 110;
      
      int e[N],ne[N],h[N],idx;
      int v[N],w[N];
      int f[N][N];
      int n,m,root;
      
      void add(int a,int b)  //存图
      {
          e[idx] = b,ne[idx] = h[a],h[a] = idx++;
      }
      
      void dfs(int u)  //递归
      {
          for(int i=h[u];~i;i=ne[i])  //遍历子节点
          {
              int son = e[i];  //取子节点
              dfs(son);
              for(int j=m-v[u];j>=0;j--)  //确保能取到当前父节点 预留v[u]的体积
              {  
                  for(int k=0;k<=j;k++)  //子节点占用的体积
                  {
                      //01背包 j从大到小 求f[u][j]时f[u][j-k]还没更新 即当前子节点没有算进去
                      f[u][j] = max(f[u][j] , f[u][j-k] + f[son][k]);
                  }
              }
          }
          //所有能放当前父节点的都放一下
          for(int j=m;j>=v[u];j--) f[u][j] = f[u][j-v[u]] + w[u]; 
          //所有放不了父节点的全部清空 放不了父节点-->什么都放不了
          for(int j=0;j<v[u];j++) f[u][j] = 0;
      }
      int main()
      {
          cin>>n>>m;
          memset(h, -1, sizeof h);
          for(int i=1;i<=n;i++)
          {
              int p;
              cin>>v[i]>>w[i]>>p;
              if(p == -1) root = i;
              else add(p,i);
          }
          
          dfs(root);
          cout<<f[root][m]<<endl;
          return 0;
      }