动态规划算法典型应用之背包问题

动态规划算法典型应用之背包问题

  • 问题描述:一个旅行者有一个背包,背包的大小为b,可以放入背包的物品有n种,物品j的重量和价值分别为 w j w_j wj v j v_j vj 请问怎么选择放入背包的物品以使得背包的价值最大?

  • 有了之前投资问题的基础,我们很容易发现,背包问题也可以用动态规划算法来解决。动态规划算法的前期准备步骤主要是如何确定子问题的规模,用什么参数来确定,以及用几个参数。和投资问题的两个参数(投资前k个,投x万元)相类似,背包问题也要用两个参数,分别是选择前k个物品,背包的空间为y 这两个参数决定。我们用优化函数 F k ( y ) F_k(y) Fk(y)来表示只允许装前k种物品,背包总数不超过y时背包的最大价值。我们计算这个规模的问题时,分两种情况考虑,如果不选择第k个物品,只选择前k-1种物品,而背包仍然是y,也就是 F k − 1 ( y ) F_{k-1}(y) Fk1(y) ;还有一种情况就是选择了第k个物品,那么剩下的背包空间就是y-w[k],而因为每个物品可以选择多个,所以剩下的背包空间仍然可以在前k个物品中进行选择,那么取得的最大值就是 F k ( y − v [ k ] ) F_k(y-v[k]) Fk(yv[k])

  • 所以最后的递推方程就是 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}

  • 我们可以看到背包问题的递推方程在max里面的两个值都运用到了之前规模比较小的子问题,第一个 F k − 1 ( y ) F_{k-1}(y) Fk1(y) 运用到了 物品种类(k - 1)这个规模参数,而第二个 F k ( y − v k ) + w k F_k(y-v_k)+w_k Fk(yvk)+wk则在相同物品种类规模k下,运用了背包大小( y − v k y-v_k yvk)这个规模参数,可以说将规模小的子问题运用得淋漓尽致。而投资问题的递推方程 F k ( x ) = m a x 0 ⩽ x k ⩽ x { f k ( x k ) + F k − 1 ( x − x k ) } F_k(x) = \underset {0\leqslant x_k \leqslant x} {max} \{f_k(x_k) + F_{k-1}(x-x_k)\} Fk(x)=0xkxmax{fk(xk)+Fk1(xxk)} 中因为max中第一个值 f k ( x k ) f_k(x_k) fk(xk)收益函数是已知条件,虽然第二个值 F k − 1 ( x − x k ) F_{k-1}(x-x_k) Fk1(xxk)同时运用了项目种类(k-1)这个规模参数和投资钱数 x − x k x-x_k xxk这个规模参数,但是昨天我在写投资问题博客的时候,感知没有那么强,只注意到了 F k − 1 F_{k-1} Fk1这个项目种类的规模参数,让人忽略了投资钱数这个规模参数。

  • 以下是我根据书上的数据,写的程序

    //背包问题 旅行者有一个背包,可以放入背包的物品有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 tag[5][11] = {0};         //表示计算优化函数F[k][y]所用到的物品的最大标号
        for (int i = 1; i <= 10; i++) //当只取第一种物品的时候,用当前钱购买尽可能多的数量,便能取得最大收益
        {
            F[1][i] = i / w[1] * v[1];
            if (i / w[1] > 0)
                tag[1][i] = 1; //表示所选择物品的最大标号
            else
                tag[1][i] = 0;
        }
        for (int i = 2; i <= 4; i++) //枚举选择前2个物品..前3个..前4个
        {
            for (int j = 1; j <= 10; j++) //枚举背包大小 从1到10
            {
                int tmp;
                if (j - w[i] < 0) //在递推过程种j-w[i]可能会产生负值,这意味着背包刺客能够承受的重量已经小于第k种物品的重量,实际上时不能装的,通过令F[i][j - w[i]]为一个很小的值,从而使 这个值在另一个优化函数比较时被淘汰,从而使得这种装法被排除
                    tmp = -114514;
                else
                    tmp = F[i][j - w[i]];
                if (F[i - 1][j] > (tmp + v[i])) //比较选择第i个物品或者不选择第i个物品哪个价值更大
                {
                    F[i][j] = F[i - 1][j];
                    tag[i][j] = tag[i - 1][j]; //标明取得最大收益时的物品没有用到i,所以和tag[i - 1][j]的标号相同
                }
                else
                {
                    F[i][j] = (F[i][j - w[i]] + v[i]);
                    tag[i][j] = i; //最大物品编号为i
                }
            }
        }
        cout << "最大收益:" << F[4][10] << endl;
        int x[5] = {0}; //用来统计每个物品被选择的数量,初始都为零
        int space = 10; //一共有十个空间
        while (1)
        {
            int i = tag[4][space];
            x[i]++;
            space -= w[i];
            if (space == 0 || i == 0)
                break;
        }
        for (int i = 1; i <= 4; i++)
            cout << "第" << i << "个物品的数量:" << x[i] << endl;
        return 0;
    }
    
  • 运行结果

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-u1VXUPBe-1618207580881)(动态规划算法典型应用之背包问题.assets/2.png)]

  • 我希望编程不是为了完成老师布置的题目,而是自己发自真地想要学习并掌握某个算法。

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值