• 背包学习之背包九讲


    做了一些icpc题目,感受到动态规划的重要性,在此学习背包
    参考至博客背包九讲

    背包九讲

    • 01背包问题
    • 完全背包问题
    • 多重背包问题
    • 混合背包问题
    • 二维费用的背包问题
    • 分组背包问题
    • 有依赖的背包问题
    • 背包问题求方案数
    • 求背包问题的具体方案

    01完全背包:

    问题:n个物品(体积和价值分别为v[i],w[i])和一个体积为V的背包,求背包装到的最大价值
    状态数组:dp[i][j]为选前i个所用体积为j的最大价值

    for(int i=1;i<=n;++i)
          for(int j=1;j<=V;++j)
                dp[i][j]=max(dp[i][j],dp[i-1][j-v[i]]+w[i]);
    

    滚动数组优化:

    for(int i=1;i<=n;++i)
          for(int j=V;j>=vi;--j)
                dp[j]=max(dp[j],dp[j-v[i]])+w[i]
    

    完全背包:

    问题:n种物品(每种无限个,体积和价值分别为v[i],w[i])和一个体积为V的背包,求背包最大价值
    状态数组:dp[i][j]为选到前i种所用体积为j的最大价值

    for(int i=1;i<=n;++i)
          for(int j=0;j<=V;++j)
                for(int k=1;k*v[i]<=j;++k)
                      dp[i][j]=max(dp[i][j],dp[i-1][j-k*v[i]]+k*w[i]);
    

    时间优化

    for(int i=1;i<=n;++i)
          for(int j=0;j<=V;++j)
          {
                dp[i][j]=dp[i-1][j];
                if(j>=v[i]) dp[i][j]=max(dp[i][j],dp[i][j-v[i]]+w[i]);
          }
    

    滚动数组空间优化

    for(int i=1;i<=n;++i)
          for(int j=V;j>=v[i];--j)
                dp[j]=max(dp[j],dp[j-v[i]]+w[i]);
    

    多重背包

    问题:n种物品(每种有限个,数量,体积和价值分别为s[i],v[i],w[i])和一个体积为V的背包,求背包最大价值
    状态数组:dp[i][j]为选到前i种所用体积为j的最大价值

    for(int i=1;i<=n;++i)
          for(int j=0;j<=V;++j)
                for(int k=0;k*v[i]<=j&&k<=s[i];++k)
                      dp[i][j]=max(dp[i][j],dp[i-1][j-k*v[i]]+k*w[i]);
    

    空间优化

    for(int i=1;i<=n;++i)
          for(int j=V;j>=0;--j)
                for(int k=0;k*v[i]<=j&&k<=s[i];++k)
                      dp[j]=max(dp[j],dp[j-k*v[i]]+k*w[i]);
    

    二进制时间优化
    将数量为s[i]的物品分成s[i]=1+2+4+8+...+x,然后跑01背包,复杂度由n3优化为n2logn

    代码略

    多重背包

    问题:既有无限个的,又有单个的,又有多个的,仍然是背包V,求最大价值
    单个的可看成多重背包的特殊状况,然后有限个和无限个分别讨论
    直接上一维:dp[j]代表用j体积装到的最大价值

    for(int i=1;i<=n;++i) cin>>v[i]>>w[i]>>s[i];//当s[i]=0时,为无限个
    for(int i=1;i<=n;++i)
          {
                if(s[i]==0)
                {
                      for(int j=V;j>=v[i];--j) dp[j]=max(dp[j],dp[j-v[i]]+w[i]);
                }
                else
                {
                      for(int k=1;k<=s[i];s[i]-=k,k*=2)
                           {
                                  for(int k=V;j>=k*v[i];--j)
                                        dp[j]=max(dp[j],dp[j-k*v[i]]+w[i]);
                           }
                      if(!s[i]) continue;
                      for(int j=V;j>=s[i]*v[i];j--)
                            dp[j]=max(dp[j],dp[j-s[i]*v[i]]+s[i]*w[i]);
                }
          }
    

    二维费用的背包问题

    问题:n个物品(体积,重量和价值分别为v[i],m[i],w[i])和一个体积为V,限重为M的背包,求背包最大价值
    问题:n种物品(每种有限个,数量,体积和价值分别为s[i],v[i],w[i])和一个体积为V的背包,求背包最大价值
    加一维就行了
    状态数组:dp[i][j]代表体积为i,载重为j时的最大价值

    for(int i=1;i<=n;++i)
          for(int j=V;j>=v[i];j--)
                for(int k=M;k>=m[i];--k)
                      dp[j][k]=max(dp[j][k],dp[j-v[i]][k-m[i]]+w[i]);
    

    分组背包

    问题:n堆物品,每堆s[i]个,每个体积和价值为v[i],w[i],和一个体积为V背包,每堆物品只能选一个,求最大价值

    分组背包看起来没什么用,实际上是各种DP题的热门考点,特别是树形DP
    一维状态:dp[j]代表用j体积的最大价值

    for(int i=1;i<=n;++i)
          for(int j=V;j>=0;--j)
                for(int k=1;k<=s[i];++k)
                      if(j>=v[k])dp[j]=max(dp[j],dp[j-v[k]]+w[k]);
    

    例题:P20142020icpc南京M

    有依赖的背包问题

    问题:n个物体之间存在着依赖关系,例如依赖关系是一棵树,选择一个节点需要选择其父亲节点

    实质上就是一道赤果果的树形DP

    状态方程:dp[x][y] 代表x节点选了体积为y的最大价值

    void dfs(int s)
    {
          for(auto v:edge[s])
          {
                dfs(v);
                for(int j=V;j>=v[s];--j)
                      for(int k=0;k<=j-v[s];++k)
                            dp[s][j]=max(dp[s][j],dp[s][j-k]+dp[v][k]); 
          }
    }
    
    

    第二类问题:n节点数选m点,选一个点必须选父节点,使总价值最大

    状态方程:dp[x][i] 代表x节点选择了i个节点的最大价值,答案就是dp[1][m]

    void dfs(int s)
    {
          for(auto v:edge[s])
          {
                dfs(v);
                for(int i=m;i>=1;--i)
                      for(int j=0;j<i;++j)
                            dp[s][i]=max(dp[s][i],dp[s][i-j]+dp[v][j]);
          }
    }
    
    背包问题求方案数

    问题:n个物品(体积和价值分别为v[i],w[i])和一个体积为V的背包,求选取价值达到最大的方案数
    只需在原来的基础上加一个计数的数组即可

    for(int i=1;i<=n;++i)
          for(int j=V;j>=v[i];--j)
                {
                      if(dp[j-v[i]]+w[i]>dp[j])
                      {
                            dp[j]=dp[j-v[i]]+w[i];
                            num[j]=num[j-v[i]];
                      }
                      else if(dp[j-v[i]]+w[i]==dp[j])
                            num[j]+=num[j-v[i]];
                }
    

    背包问题求出具体方案

    问题:n个物品(体积和价值分别为v[i],w[i])和一个体积为V的背包,求选取价值达到最大的方案

    for(int i=1;i<=n;++i)
          for(int j=V;j>=vi;--j)
                dp[j]=max(dp[j],dp[j-v[i]])+w[i];
    int val=V;
    for(int i=n;i>=1;--i)
          if(val-v[i]>=0&&dp[val-v[i]]+w[i]==dp[val]) 
                cout<<i<<" ";
    
    

    如果题目有要求字典序最小,那求解01背包的时候应当倒着求。

  • 相关阅读:
    MYSQL数据库导入数据时出现乱码的解决办法
    Java Web(一) Servlet详解!!
    hibernate(九) 二级缓存和事务级别详讲
    MySQL(五) MySQL中的索引详讲
    LinkedHashMap源码详解
    hibernate(八) Hibernate检索策略(类级别,关联级别,批量检索)详解
    hibernate(七) hibernate中查询方式详解
    MySQL(四) 数据表的插入、更新、删除数据
    MySQL(三) 数据库表的查询操作【重要】
    MySQL(二) 数据库数据类型详解
  • 原文地址:https://www.cnblogs.com/Tiork/p/14191875.html
Copyright © 2020-2023  润新知