• 背包问题之01背包


    sicily 1146 采药

    还是用这题来说吧,对01背包的分析看我之前那篇就好了http://www.cnblogs.com/dominjune/p/4383762.html

    这里主要是想改进一下二维数组的做法,用一维数组来实现01背包,也叫做滚动数组!

    先借用某位大牛的一句话:“01背包在二维数组上操作,就是为了防止一个物品被放入多次的情况“

    但其实01背包也可以用一维数组来做啦!

    先看代码:

     1 #include <iostream>
     2 
     3 using namespace std;
     4 
     5 int w[105], val[105];
     6 int dp[1005];
     7 
     8 int main()
     9 {
    10     int t, m, res=-1;
    11     cin >> t >> m;
    12     for(int i=1; i<=m; i++)
    13     {
    14         cin >> w[i] >> val[i];
    15     }
    16     for(int i=1; i<=m; i++) //物品 
    17         for(int j=t; j>=0; j--) //容量,逆序
    18         {
    19             if(j >= w[i])
    20                 dp[j] = max(dp[j-w[i]]+val[i], dp[j]);
    21         
    22         }
    23     cout << dp[t] << endl;
    24     return 0;
    25 }

    这里是把状态只用一维数组来表示,dp[j]表示放到第j个物品(或者说是前j个物品)的时候的最大价值,少了一维,感觉好神奇...不过在我仅仅做过的题目中,好像有很多都是用滚动数组的形式...

    好了,用回当年的辣个栗子:

    ----------------------------------------

    让我假设现在的背包的容量是C=10;

    物品编号: 1 2 3

    物品重量: 5 6 4

    物品价值:20 10 12

    ---------------------------------------

    直接分析dp数组:

    dp:0 0 0 0 0 0 0 0 0 0

    i=1:

    dp[10] = max(dp[5]+20, dp[10]);

    dp[9] = max(dp[4]+20, dp[9]);

    dp[8] = max(dp[3]+20, dp[8]);

    dp[7] = max(dp[2]+20, dp[7]);

    dp[6] = max(dp[1]+20, dp[6]);

    dp[5] = max(dp[0]+20, dp[5]);

    dp: 0 0 0 0 20 20 20 20 20 20

    ---------------------------------------------

    i=2:

    dp[10] = max(dp[6]+4, dp[10]);

    dp[9] = max(dp[3]+10, dp[9]);

    dp[8] = max(dp[2]+10, dp[8]);

    dp[7] = max(dp[1]+10, dp[7]);

    dp[6] = max(dp[0]+10, dp[6]);

    dp: 0 0 0 0 20 20 20 20 20 20 //看到了没,选10的都被之前的20压下去了

    -------------------------------------------

    i=3:

    dp[10] = max(dp[6]+12, dp[10]);

    dp[9] = max(dp[5]+12, dp[9]);

    dp[8] = max(dp[4]+12, dp[8]);

    dp[7] = max(dp[3]+12, dp[7]);

    dp[6] = max(dp[2]+12, dp[6]);

    dp[5] = max(dp[1]+12, dp[5]);

    dp[4] = max(dp[0]+12, dp[4]);

    dp: 0 0 0 12 20 20 20 20 32 32

    -----------------------------------------

    dp[10]就是背包容量为10的时候的最大价值,就是要求的值了,可以看到,容量大的时候的值取决于容量小的时候的值,从而不断被正确更新,所以用滚动数组的时候,j的循环必须是从大到小逆序开始的,逆序,就防止了一个物品放入多次!!!否则...........

    直接分析dp数组:

    dp:0 0 0 0 0 0 0 0 0 0

    i=1:

    dp[5] = max(dp[0]+20, dp[5]);

    dp[6] = max(dp[1]+20, dp[6]);

    dp[7] = max(dp[2]+20, dp[7]);

    dp[8] = max(dp[3]+20, dp[8]);

    dp[9] = max(dp[4]+20, dp[9]);

    dp[10] = max(dp[5]+20, dp[10]);

    dp: 0 0 0 0 20 20 20 20 20 40 //看到问题了吗!dp[10]不仅仅是由dp[5]决定了,因为dp[5]还被dp[0]更新了一次,相当于,i=1时,即只有一个物品时,这个物品拿了两次,完全不符合01背包了,但是,这个却是我们后面要提到的完全背包!接着看:

    ---------------------------------------------

    i=2:

    dp[6] = max(dp[0]+10, dp[6]);

    dp[7] = max(dp[1]+10, dp[7]);

    dp[8] = max(dp[2]+10, dp[8]);

    dp[9] = max(dp[3]+10, dp[9]);

    dp[10] = max(dp[4]+10, dp[10]);

    dp: 0 0 0 0 20 20 20 20 20 40 

    -------------------------------------------

    i=3:

    dp[4] = max(dp[0]+12, dp[4]);

    dp[5] = max(dp[1]+12, dp[5]);

    dp[6] = max(dp[2]+12, dp[6]);

    dp[7] = max(dp[3]+12, dp[7]);

    dp[8] = max(dp[4]+12, dp[8]);

    dp[9] = max(dp[5]+12, dp[9]);

    dp[10] = max(dp[6]+12, dp[10]);

    dp: 0 0 0 12 20 20 20 24 32 40

    ------------------------------------------ 

    (其实后面不用看都知道不对了,不过我写了就懒得删掉......)

  • 相关阅读:
    读《编写可维护的JavaScript》第七章总结
    读《编写可维护的JavaScript》第六章总结
    最新Blog
    cnUVA情况
    Casio普通计算器编程
    大家AK杯 灰天飞雁NOIP模拟赛题解/数据/标程
    Pascal <-> C/C++ 转换简明教程
    [互动][扫盲]信息学奥林匹克竞赛是什么
    以后这个博客可能不会用啦 请到新地址...
    算法专题训练 搜索a-T3 Ni骑士(ni)
  • 原文地址:https://www.cnblogs.com/dominjune/p/4401985.html
Copyright © 2020-2023  润新知