0-1背包二维转一维

01背包的理解,二维数组化一维数组的理解(附hdu2602 Bone Collector)

标签: 动态规划 dp hdu acm
1016人阅读 评论(0) 收藏 举报
(function () {
        try
        {
            var lib = eval("("+
(function () {        try        {            var lib = eval("("+
("#lib").attr("value")+")"); var html = ""; if (lib.err == 0) { $.each(lib.data, function (i) { var obj = lib.data[i]; //html += ' ' + obj.name + "  "; html += ' '; html += ' '; html += ' ' + obj.name + ''; html += ' '; }); if (html != "") { setTimeout(function () { ("#lib").html(html); ("#lib").html(html); ("#embody").show(); }, 100); } } } catch (err) { } });
            <div class="category clearfix">
        <div class="category_l">
            <img src="https://csdnimg.cn/release/phoenix/images/category_icon.jpg">
            <span>分类:</span>
        </div>
        <div class="category_r">
                                        <label onclick="GetCategoryArticles('6328652','qq_33279781','top','52073519');">
                <span onclick="_gaq.push(['_trackEvent','function', 'onclick', 'blog_articles_fenlei']);">
                   动态规划<em>(3)</em>
                </span>
                <img class="arrow-down" src="https://csdnimg.cn/release/phoenix/images/arrow_triangle _down.jpg" style="display:inline;">
                <img class="arrow-up" src="https://csdnimg.cn/release/phoenix/images/arrow_triangle_up.jpg" style="display:none;">
                <div class="subItem">
                    <div class="subItem_t">
                        <a href="https://blog.youkuaiyun.com/qq_33279781/article/category/6328652" target="_blank">作者同类文章</a>
                        <i class="J_close">X</i>
                    </div>
                    <ul class="subItem_l" id="top_6328652"></ul>
                </div>
            </label>
                                                        <label onclick="GetCategoryArticles('6161985','qq_33279781','top','52073519');">
                <span onclick="_gaq.push(['_trackEvent','function', 'onclick', 'blog_articles_fenlei']);">
                   算法<em>(12)</em>
                </span>
                <img class="arrow-down" src="https://csdnimg.cn/release/phoenix/images/arrow_triangle _down.jpg" style="display:inline;">
                <img class="arrow-up" src="https://csdnimg.cn/release/phoenix/images/arrow_triangle_up.jpg" style="display:none;">
                <div class="subItem">
                    <div class="subItem_t">
                        <a href="https://blog.youkuaiyun.com/qq_33279781/article/category/6161985" target="_blank">作者同类文章</a>
                        <i class="J_close">X</i>
                    </div>
                    <ul class="subItem_l" id="top_6161985"></ul>
                </div>
            </label>
                                    </div>
    </div>
            <div class="bog_copyright">
                    <p class="copyright_p">
            版权声明:本文为博主原创文章,未经博主允许不得转载。                https://blog.youkuaiyun.com/qq_33279781/article/details/52073519            </p>
                </div>
    <div style="clear:both"></div><div style="border:solid 1px #ccc; background:#eee; float:left; min-width:200px;padding:4px 10px;"><p style="text-align:right;margin:0;"><span style="float:left;">目录<a href="https://blog.youkuaiyun.com/qq_33279781/article/details/52073519" title="系统根据文章中H1到H6标签自动生成文章目录">(?)</a></span><a href="#" onclick="javascript:return openct(this);" title="展开">[+]</a></p><ol style="display:none;margin-left:14px;padding-left:14px;line-height:160%;"><li><a href="https://blog.youkuaiyun.com/qq_33279781/article/details/52073519#t0">01背包问题</a></li><li><a href="https://blog.youkuaiyun.com/qq_33279781/article/details/52073519#t1">贪心是不行的举个反例</a></li><li><a href="https://blog.youkuaiyun.com/qq_33279781/article/details/52073519#t2">但是我们还能将空间复杂度降低从二维降为一维</a></li><li><a href="https://blog.youkuaiyun.com/qq_33279781/article/details/52073519#t3">Bone Collector</a></li></ol></div><div style="clear:both"></div><div id="article_content" class="article_content csdn-tracking-statistics" data-pid="blog" data-mod="popu_307" data-dsm="post">
                <div class="markdown_views">
            <h3 id="01背包问题"><a name="t0" target="_blank"></a>01背包问题:</h3>

有n个物品和一个容量为v的背包,用val[i]表示第i个物品的价值,用vol[i]表示第i个物品的体积,那么,如何使背包里装的物品的总价值最大呢?

贪心是不行的,举个反例:

n=3, v=100

val[i]vol[i]
8060
5050
5050

按照val[i]/vol[i]比值从大到小贪心,那么会得到错误答案80,但是正确答案是100

动态规划的思想:

memset(dp, 0, sizeof(dp));
for(int i=0; i<n; i++){
    for(int j=0; j<=v; j++){
    if(j>=vol[i])
        dp[i+1][j] = max(dp[i][j], dp[i][j-vol[i]]+val[i]);
        else
        dp[i+1][j] = dp[i][j];
    }
}
  
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

如何理解这段代码呢?我们设dp[i][j]为前i件物品放在容量为j的背包里所能得到的最大价值,那么思考一下,每个物品都只有两种可能,放或不放,这就是为什么叫做01背包的原因。那么,我们将第i件物品放在容量为j的背包中,使dp[i][j]最大,那么对于第i件物品,也只有两种操作。于是,我们可以很容易想到,我们不放第i件物品时,是不是需要先知道dp[i-1][j]的值呢?这就形成了dp,形成了一种递推关系。另一种可能,假设我们放第i件物品,那么,首先需要考虑当前容量j是否放得下物品i,假设放得下,将j减去vol[i],即在剩余的体积放前i件所得到的最大的价值为dp[i][j-vol[i]],所以总价值为dp[i][j-vol[i]] +val[i]。

那么,状态转移方程为:

dp[i+1][j] = max(dp[i][j], dp[i][j-vol[i]]+val[i])

或者:
dp[i][j] = max(dp[i-1][j], dp[i-1][j-vol[i]]+val[i])

这两种写法要注意数组是从0开始还是从1开始
  
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

明显的,时间复杂度是O(n*v)

但是我们还能将空间复杂度降低,从二维降为一维。

看下面这段代码:

memset(dp, 0, sizeof(dp));
for(int i=0; i<n; i++){
    for(int j=v; j>=vol[i]; j--){
    dp[j] = max(dp[j], dp[j-vol[i]]+val[i])
    }
}
  
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

如何理解二维降一维呢?对于外层循环中的每一个i值,其实都是不需要记录的,在第i次循环时,所有的dp[0…v]都还未更新时,dp[j]还记录着前i-1个物品在容量为j时的最大价值,这样就相当于还记录着dp[i-1][j]和dp[i-1][j-vol[i]]+val[i]。

为什么要从v开始递减遍历?我举个例子,假设一个物品GG价值1000,体积为2,那么假设我们按【0…..v】这个顺序遍历,那么在j=2时,dp[2] = max(dp[2], dp[0]+1000),那么dp[2] = 1000,当j=4时,dp[4]=max(dp[4], dp[2]+1000), dp[4] = 2000,这时我们再思考一下,GG将被放进背包两次!!,如果我们逆序遍历,就可以避免这种结果。

此外,这里可以进行一个常数优化,将j>=vol[i]写进for循环中。

大家可以看一下hdu2602这一题,是一题单纯的01背包。

题目:

Bone Collector

Problem Description
Many years ago , in Teddy’s hometown there was a man who was called “Bone Collector”. This man like to collect varies of bones , such as dog’s , cow’s , also he went to the grave …
The bone collector had a big bag with a volume of V ,and along his trip of collecting there are a lot of bones , obviously , different bone has different value and different volume, now given the each bone’s value along his trip , can you calculate out the maximum of the total value the bone collector can get ?

Input
The first line contain a integer T , the number of cases.
Followed by T cases , each case three lines , the first line contain two integer N , V, (N <= 1000 , V <= 1000 )representing the number of bones and the volume of his bag. And the second line contain N integers representing the value of each bone. The third line contain N integers representing the volume of each bone.

Output
One integer per line representing the maximum of the total value (this number will be less than 231).

Sample Input
1
5 10
1 2 3 4 5
5 4 3 2 1

Sample Output
14

用二维数组解:

#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>

using namespace std;
const int maxn = 1000+10;
int val[maxn];
int vol[maxn];
int dp[maxn][maxn];

int main(){
    int t, n, v;
    cin>>t;
    while(t--){
        cin>>n>>v;
        memset(vol, 0, sizeof(vol));
        memset(val, 0, sizeof(val));
        memset(dp, 0, sizeof(dp));

        for(int i=0; i<n; i++)
            cin>>val[i];
        for(int i=0; i<n; i++)
            cin>>vol[i];

        for(int i=0; i<n; i++){
            for(int j=0; j<=v; j++){
                if(j>=vol[i])
                    dp[i+1][j] = max(dp[i][j], dp[i][j-vol[i]]+val[i]);
                else
                    dp[i+1][j] = dp[i][j];
            }
        }
        cout<<dp[n][v]<<endl;
    }
    return 0;
}
  
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37

用一维数组解:

#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>

using namespace std;
const int maxn = 1000+10;
int val[maxn];
int vol[maxn];
int dp[maxn];

int main(){
    int t, n, v;
    cin>>t;
    while(t--){
        cin>>n>>v;
        memset(vol, 0, sizeof(vol));
        memset(val, 0, sizeof(val));
        memset(dp,  0, sizeof(dp));
        for(int i=0; i<n; i++)
            cin>>val[i];
        for(int i=0; i<n; i++)
            cin>>vol[i];

    <span class="hljs-keyword">for</span>(<span class="hljs-keyword">int</span> i=<span class="hljs-number">0</span>; i&lt;n; i++){
        <span class="hljs-keyword">for</span>(<span class="hljs-keyword">int</span> j=v; j&gt;=vol[i]; j--){
            dp[j] = max(dp[j], dp[j-vol[i]]+val[i]);
        }
    }
    <span class="hljs-built_in">cout</span>&lt;&lt;dp[v]&lt;&lt;endl;
}
<span class="hljs-keyword">return</span> <span class="hljs-number">0</span>;

}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33






评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值