背包问题第二种形式的递推方程

背包问题第二种形式的递推方程

Ⅰ.第一种递推方程

  • 背包问题是动态规划问题的经典问题,在我之前的博客里已经讨论过。正常的递推方程是这样的。

    F k ( y ) = m a x { F k − 1 ( y ) , F k ( y − v k ) + w k } F_k(y) = max\{F_{k-1}(y),F_k(y-v_k)+w_k\} Fk(y)=max{Fk1(y),Fk(yvk)+wk}

  • 其主要思想是这样的,第k种物品选不选,如果不选的话,那么就是 F k − 1 ( y ) F_{k-1}(y) Fk1(y)。如果选的话,就是 F k ( y − v k ) + w k F_k(y-v_k)+w_k Fk(yvk)+wk,我们注意到这时F的下标仍然是k,因为在取得最大价值的解法里第k种物品可能被选择了多次。如果变成了 F k − 1 ( y − v k ) + w k F_{k-1}(y-v_k)+w_k Fk1(yvk)+wk,那么说明第k种物品只能选择一次,变成01背包问题了。

Ⅱ.第二种递推方程

  • 在学习算法与设计4.3对贪心法得不到最优解情况的处理这一节中,我看到了一个找零钱问题,大体是这样一个问题:有n种不同的硬币,它们的币值各不相同,第一个硬币的币值是1。现在需要用这些硬币付款的总数是Y,让我们求如何选择硬币使得付钱的硬币最轻。

  • 我们看到这和背包问题非常类似。找两个规模参数,一个选择前k个硬币,一个是总钱数y。所以我写下了背包问题相同的递归方程,也就是上文写的。但是我惊奇的发现书上的递归方程不太一样。

    F k + 1 = m i n 0 ⩽ x k + 1 ⩽ ⌊ y v k + 1 ⌋ { F k ( y − v k + 1 x k + 1 ) + w k + 1 x k + 1 } F_{k+1}=\underset {0\leqslant x_{k+1} \leqslant \lfloor \frac{y}{v_{k+1}} \rfloor}{min} \{F_k({y-v_{k+1}x_{k+1})+w_{k+1}x_{k+1}}\} Fk+1=0xk+1vk+1ymin{Fk(yvk+1xk+1)+wk+1xk+1}

  • 其中v表示硬币的币值,w表示某硬币的个数,x表示选择某硬币的个数

  • 思考过后,我发现这是背包问题的第二种形式的递推方程,和原始的递推方程等价。

  • 于是我把背包问题的代码按照这个递推方程改了一下,得到了和之前一样的结果。

    //背包问题 旅行者有一个背包,可以放入背包的物品有n种,物品j的重量和价值分别是w[j]和v[j].如果背包的最大重量限制是b,怎么样选择放入背包的物品使得背包的价值最大?
    //输入 每个物品的体积和价值 和背包限制b
    //输出 最大价值和具体每个物品如何选取
    #include <iostream>
    #include <algorithm>
    using namespace std;
    
    int main()
    {
        int w[5] = {0, 2, 3, 4, 7};   //每个物品的重量w[0]弃用
        int v[5] = {0, 1, 3, 5, 9};   //每个物品的价值v[0]弃用
        int b = 10;                   //背包限制
        int F[5][11] = {0};           //F[k][y]表示在选择前k个物品,并且背包限重y时,取得的最大收益
        int number[5][11] = {0};      //number[k][y]表示当背包限重为y时,选择k这个物品的个数
        for (int i = 1; i <= 10; i++) //当只取第一种物品的时候,用当前钱购买尽可能多的数量,便能取得最大收益
        {
            number[1][i] = i / w[1]; //选择第一种物品的个数
            F[1][i] = i / w[1] * v[1];
        }
        for (int i = 2; i <= 4; i++) //枚举选择前2个物品..前3个..前4个
        {
            for (int j = 1; j <= 10; j++) //枚举背包大小 从1到10
            {
                for (int x = 0; x <= j / w[i]; x++) //枚举选择第k个物品有几个
                {
                    int tmp;
                    if (j - x * w[i] >= 0)                       //如果能够放下x个物品i
                        tmp = F[i - 1][j - x * w[i]] + v[i] * x; //代表第k个物品选x个,剩下的空间从前k-1个物品中选择
                    else
                        tmp = 0;
                    if (tmp > F[i][j])
                    {
                        number[i][j] = x;
                        F[i][j] = tmp;
                    }
                }
            }
        }
        cout << "最大收益:" << F[4][10] << endl;
        int space = 10;
        for (int i = 4; i > 0; i--)
        {
            cout << "第四件物品:" << number[i][space] << endl;
            space -= number[i][space] * w[i];
        }
        return 0;
    }
    
  • 运行结果

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-T3SVH57R-1618756356127)(背包问题第二种形式的递推方程.assets/运行结果.png)]

  • 2
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值