动态规划——0-1背包问题

0-1背包问题状态转移方程 f[i][j] = Max{f[i-1][j],f[i-1][j-w[i]]+v[i]}

其中 w[i] 为第i件物品的重量,v[i]为第i件物品的价值

f[i][j] 表示 从前 i 件物品中选出若干件放到承重为 j 的袋子中所能达到的最大价值


每件物品就只有两个状态:放与不放

为了使价值最大化 我们要考虑第i件物品放不放进袋子中(放与不放有两个影响:1、影响袋子的承重;2、影响袋子的总价值;所以不要觉得价值大就一定放,它可能消耗很大的承重而导致最终的结果不是最优)


为了便于理解 可以为 f[i][j] 建一个表格 动态规划的过程其实就是这张表的填表过程

现在假设袋子的承重为10,一共有五件物品,其中:

w[1-5] = {2,2,6,5,4}  v[1-5] = {6,3,5,4,6} 求解袋子价值的最大值


表格:

w[1-5] = {2,2,6,5,4}

v[1-5] = {6,3,5,4,6}

  1  2  3  4  5  6  7  8  9  10

1 0  6  6  6  6  6  6  6  6  6
2 0  6  6  9  9  9  9  9  9  9
3 0  6  6  9  9  9  9  11 11 14
4 0  6  6  9  9  9  10 11 13 14
5 0  6  6  9  9  12 12 15 15 15


表格是这么填的 比如先填第一行 [1,1] 就是现在可选的物品就第1件 袋子承重为1 最大价值则为0

即 [1,1] = 0 因为 这件物品重量为 2 不能放

以此类推 [1,2] 就是现在可选的物品就第一件 承重 为2 最大价值则为 6 现在可以放了 那么后面的

就全为6了 承重为 3-10 的当然可以放下这件重量为2的物品 所以它们的最大价值都为 6

第二行 [2,1] 就是现在可选的物品是第1、2件 袋子承重为 1 最大价值则为0,因为这两件物品重量

都大于1  [2,2] 还是可选前两件 但选哪件呢?还是都选?当然不能都选 都选重量为 4 > 2 只能选一件

就选价值为6的第一件 因为它的价值大于第2件的3 后面全部依次类推。


/*
有五件物品,它们的重量分别是2,2,6,5,4,它们的价值分别是6,3,5,4,6,现在给你个承重为10的背包,如何让背包里装入的物品具有最大的价值总和?
解:15
*/
#include <iostream>
#include <cstdio>
#include <memory.h>
using namespace std;


int v[6] = {0,6,3,5,4,6}; //下标0不用
int w[6] = {0,2,2,6,5,4};
//int v[6] = {0,6,4,5,3,6};
//int w[6] = {0,4,5,6,2,2};
int f[6][11];
int flag[6][11];
/*flag[i][j] 表示 从 前i件物品中挑若干件物品到承重为j的袋子中 所得价值最大时
是否选择了 第 i 件物品 如果选了 则flag[i][j] = 1 否则为0 */


int main()
{
    memset(f,0,sizeof(f));
    memset(flag,0,sizeof(flag));
    int n = 5;
    for (int i = 1; i <= 5; i++)
    //for (int j = 1; j <= 10; j++)
    {
        for (int j = 1; j <= 10; j++)
        //for (int i = 1; i <= 5; i++)
        {
             //为了价值最大 考虑第i件物品放不放进袋子
               if (j-w[i] < 0) //承重为j的袋子放不下第i件物品 肯定就不放了
               {
                    f[i][j] = f[i-1][j];
               }
               else
               {
                    //注释1
                    f[i][j] = max(f[i-1][j],f[i-1][j-w[i]]+v[i]);




                    if (max(f[i-1][j],f[i-1][j-w[i]]+v[i]) == f[i-1][j-w[i]]+v[i])
                         /*
                         如果价值较大为选了第i件物品的情况
                         置flag[i][j]为1
                         */
                         flag[i][j] = 1;
               }
        }
    }
    //输出表格f[i][j]
    for (int i = 1; i <= 5; i++)
    {
        for (int j = 1; j <= 10; j++)
        {
            cout << f[i][j] << " ";
        }
        cout << endl;
    }
    cout << endl;


    //输出价值最大时所挑物品的各自重量
    //注释2
    for (int i = 5,j = 10; ; )
    {
         if (j - w[i] < 0) break;
         //if (i <= 0) break;


         if (flag[i][j]) //如果f[i][j] 为1
         {
              // 输出所选的第i件物品的重量w[i]
              cout << w[i] << " ";
              // 承重变为 j - w[i]
              j = j - w[i];
              // 可选物品除去第i件(减少一件)
              i = i - 1;
         }
         else
              //没选则承重不变 物品减少一件
              i = i - 1;
    }


    cout << endl;


    /*
    注释2
    要想知道 最大值为 f[5][10] = 15 时 袋子里装的是哪些物品 则这样思考
    flag[5][10] = 1 表示 价值最大时 第 5 件物被装到了袋子里
    那再考察 f[4][10-w[5]] 是不是等于1?如果是 则第4件也被装进袋子里
    否则没装 没装就继续考察 f[3][10-w[5]] (不用减去w[4])。。。
    就这样一直往回推就行
    */
    return 0;
}
<span style="font-family: KaiTi_GB2312;font-size:18px;"></span><pre name="code" class="cpp"> /*注释1:
                    如果承重为j的袋子可以放下第i件物品 即 j>= w[i]
                    那么除去w[i]的重量 袋子剩下的承重 j-w[i] 去装
                    前i-1件物品 可以 使袋子最大价值是多少? 再加上
                    第i物品的总价值是多少? 与不放第i件物品的总价值
                    f[i-1][j]相比 哪个大? 选择较大者。 (如果给了第
                    i件物品而又不放 那就跟没有一样 相当于就是从i-1件
                    物品挑若干件放到承重为j的袋子中 使价值最大)
                    */

                    /*
                    假设 i = 4  ,  j = 10
                    前 3 件物品重量分别为  5 4 3
                               价值分别为  3 5 1
                    第 4 件物品重量为      5
                               价值为      7
                    现在考察 f[4][10] 的最大值

                    首先当承重为 10 时  从前三件挑 不能全挑 否则重量为 12 超过载重
                    则挑前两件 重量总和为 9 价值达到 8

                    而 10 > 5 即袋子可以装 第4件物品  那么如果放第i件物品 则袋子只有
                    剩下 的 5 单位承重去装 前三件物品 现在挑的话 只能 挑其中一件 挑
                    两件以上都会超重 则挑价值最大的 第2件 重量为 4 价值为 5

                    那么加上 第4件物品 总重为9 不超过承重 价值达到 5+7 = 12  >  8
                    所以当然要选择放第4件物品

                    如果 第4件物品 重量 大于 10 无论它价值多高都不能放 跟没给一样
                    则 f[i][j] 就只能等于 f[i-1][j]
                    */

 








  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
回溯法是解决0-1背包问题的一种常用方法。该问题是指在给定n种物品和一个容量为C的背包的情况下,如何选择装入背包的物品,使得装入背包中物品的总价值最大。回溯法的基本思路是搜索所有可能的解,并在搜索过程中剪枝,以达到减少搜索次数的目的。具体实现可以参考引用中的递归函数rKnap。 在回溯法中,我们首先将物品按照单位重量的价值递减排序,然后从第一个物品开始搜索。对于每个物品,我们有两种选择:将其放入背包或不放入背包。如果将其放入背包,我们需要检查当前背包容量是否足够,如果足够,则将其放入背包,并更新当前背包的重量和价值。然后递归搜索下一个物品。如果不将其放入背包,则直接递归搜索下一个物品。在搜索过程中,我们需要记录当前背包的重量和价值,以及当前最优解的最大价值。如果当前背包的价值已经超过当前最优解的最大价值,则可以剪枝,不再继续搜索。 C++代码实现可以参考以下范例: <<范例: #include <iostream> #include <algorithm> using namespace std; const int MAXN = 100; int n, c; int w[MAXN], v[MAXN]; int bestv = 0, curv = 0, curw = 0; void backtrack(int i) { if (i > n) { bestv = max(bestv, curv); return; } if (curw + w[i] <= c) { curw += w[i]; curv += v[i]; backtrack(i + 1); curw -= w[i]; curv -= v[i]; } if (curv + v[i] * (c - curw) > bestv) { backtrack(i + 1); } } int main() { cin >> n >> c; for (int i = 1; i <= n; i++) { cin >> w[i] >> v[i]; } sort(w + 1, w + n + 1); sort(v + 1, v + n + 1); backtrack(1); cout << bestv << endl; return 0; } >>

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值