01 完全 多重 背包

1.01背包

1.问题雏形

有N件物品和一个容量为V的背包。第i件物品的体积是c[i],价值是w[i]。求解将哪些物品装入背包可使价值总和最大。特点每个物品没能选一次。

Sample Input
4 10
3 7
4 15
5 20
7 25
Sample output
35

2.二维求解

dp[i]][j] 其中代表当前所选第i个物品j容量量下取得最大价值为多少。
首先只考虑第i件物品的策略(放与不放)
1.如果不放,就是求前i-1个物品放入容量为v的背包的最大价值,即f[i-1][j]的最大价值。
2.如果放,就是求前i个物品放入容量为v-v[i]的最大价值再加上w[i],即f[i][j-v[i]+w[i]的最大价值。
两者取最优,就是f[i][j]。
状态转移方程:
f[i][j]=max{f[i-1][j],f[i-1][j-v[i]]+w[i]}
此时空间复杂度为O(NM) 空间复杂度为NM;

for(int i=1;i<=n;i++)//每个物品
        {
            for(int j=1;j<=m;j++)//空间
            {
                if(j<v[i])
                {
                    dp[i][j]=dp[i-1][j]; //如果当前容量小于v[i]无法更新。
                }
                else
                {
                    dp[i][j]=max(dp[i-1][j],dp[i-1][j-v[i]]+w[i]);//更新
                }
            }
        }     

3.一维优化

1 2 3 4 5 6 7 8 9 10
1 0 0 7 7 7 7 7 7 7 7
2 0 0 7 15 15 15 22 22 22 22
3 0 0 7 15 20 20 22 27 35 35
4 0 0 7 15 20 20 25 27 35 35

由上表格模拟的二维可以看出 求解第dp[i][j]时只需要dp[i-1][j]时的状态
所以我们可以用滚动数组来优化空间

滚动数组:

每次使用固定的存储空间,用新值将原来无用的数据覆盖掉,从而压缩空间

和上面二维求解一样,最外围的for循环依然是枚举每个物品,
但对于内循环为了保证结果正确应该从 n ~ 1 进行枚举

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

2.完全背包

有N种物品和一个容量为V的背包,每种物品都有无限件可用。第i种物品的费用是c[i],价值是w[i]。求解将哪些物品装入背包可使这些物品的费用总和不超过背包容量,且价值总和最大。

可以把每个物品拆分成V/v[I]个当01背包来做但时间一定是t掉的

我们从开始考虑,对于对i件物品他的策略已经不是2取取或不取2种,而是取1种,2种…继续按01背包的思路f[i][v]仍表示前i物品放入空间j的最大价值,那么dp方程变为
f[i][j]=max(dp[i-1][j],dp[i-1][j-v[i]k+w[i]k)
时间复杂度O(v
n
k)

改变v的循环顺序,首先想想为什么01中要按照v=V…0的逆序来循环。这是因为要保证第i次循环中的状态f[i][v]是由状态f[i-1][v-c[i]]递推而来,这正是为了保证每件物品只选一次,保证在考虑“选入第i件物品”这件策略时,依据的是一个绝无已经选入第i件物品的子结果f[i-1][v-c[i]]。而现在完全背包的特点恰是每种物品可选无限件,所以在考虑“加选一件第i种物品”这种策略时,却正需要一个可能已选入第i种物品的子结果f[i][v-c[i]],所以就可以并且必须采用v= 0…V的顺序。
时间复杂度O(VN)

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

3.多重背包

有N种物品和一个容量为V的背包。第i种物品最多有n[i]件可用,每件费用是c[i],价值是w[i]。求解将哪些物品装入背包可使这些物品的费用总和不超过背包容量,且价值总和最大。

  1. 转化为01背包,将每个物品拆分成n[i]个物品
    时间复杂度O(V*∑n[i]) 明显t掉
  2. 枚举每一个循环可取的情况dp方程
    f[i][j]=max(dp[i-1][j],dp[i-1][j-v[i]*k+w[i]k)
    时间复杂度仍是O(V
    ∑n[i]);

我们希望拆分之后降低时间复杂度同时可以想01背包那样求解,考虑2进制的思想进行拆分,将第i种物品分成若干件物品,其中每件物品有一个系数,这件物品的费用和价值均是原来的费用和价值乘以这个系数。使这些系数分别为 1,2,4,…,2(k-1),n[i]-2k+1。
时间复杂度 O(V*∑log n[i])) 时间明显提升

猜你喜欢

转载自blog.csdn.net/qq_40703941/article/details/85072198