01背包

01背包

题目描述:

有N种物品和一个容量为m的背包。每件物品只能使用一次。

第i件物品的体积是v[i],价值是w[i]。求解将哪些物品装入背包,可使这些物品的体积总和不超过背包容量,且总价值最大。

 

分析:从集合角度对 01 背包问题进行分析,将情况分为含有第i个物品和不含有第i个物品,下面结合代码进行分析:

朴素版01背包代码(二维版本)(这个代码插入还挺帅QAQ)时间复杂度O(n^2)

#include<iostream>
#include<algorithm>
using namespace std;
const int N = 1010;
int v[N],w[N];
int n,m;
int f[N][N];              //总价值数组
int main()
{
    cin >> n >> m;
    for(int i = 1;i <= n;i ++)
    cin >> v[i] >> w[i];
    for(int i = 1;i <= n;i ++)  
    for(int j = 0;j <= m;j ++)
    {
        f[i][j] = f[i - 1][j]; //状态一:从前i件物品中取,取件中不包含物品i,总体积不大于j
        if(j >= v[i])
        f[i][j] = max(f[i][j] , f[i - 1][j - v[i]] + w[i]);//状态一二:从前i件物品中取,取件中包含物品i,总体积不大于j
    }
    cout << f[n][m] << endl;//从前n件物品中取物品总体积不超过m的最大价值
    return 0;
}

优化版01背包(一维的船新版本)时间复杂度O(n^2)

优化思想:运用了滚动数组的思想以及逆序遍历(较为重要)

#include<iostream>
#include<algorithm>
using namespace std;
const int N = 1010;
int f[N];
int v[N],w[N];
int n,m;
int main()
{
    cin >> n >> m;
    for(int i = 1;i <= n;i ++)
    cin >> v[i] >> w[i];
    for(int i =1;i <= n;i ++)
    for(int j = m;j >=v[i];j --)         //逆序遍历容量
    f[j] =max(f[j] , f[j - v[i]] + w[i]);//保证此时的f[j - v[i]]的状态为f[i - 1]下的
    cout << f[m] << endl;
    return 0;
}

为了学习方便,整体模拟一遍一维状态下的01背包代码:

样例:  4 5

    1 2

    2 4

    3 4

    4 5

输出:    8

 i = 1时   j >= v[ 1 ] = 1     w[1] = 2

F[ 5 ] = max(F [ 5 ] , F[ 5  -  v [1] ] + w [1] )             F[ 5 ] =F[ 4 ]  + w [ 1 ]  =  2

F[ 4 ] = max(F [ 4 ] , F[ 4  -  v [1] ] + w [1] )             F[ 4 ] =F[ 3 ]  + w [ 1 ]  =  2

F[ 3 ] = max(F [ 3 ] , F[ 3  -  v [1] ] + w [1] )             F[ 3 ] =F[ 2 ]  + w [ 1 ]  =  2

F[ 2 ] = max(F [ 5 ] , F[ 2  -  v [1] ] + w [1] )             F[ 2 ] =F[ 1 ]  + w [ 1 ]  =  2

F[ 1 ] = max(F [ 5 ] , F[ 1  -  v [1] ] + w [1] )             F[ 1 ] =F[ 0 ]  + w [ 1 ]  =  2

 

i = 2 时,j >= v[ 2 ] = 2          w[2] = 4

F[ 5 ] = max(F [ 5 ] , F[ 5  -  v [2] ] + w [2] )            F[ 5 ] =F[ 3 ]  + w [ 2 ]  =  6

F[ 4 ] = max(F [ 4 ] , F[ 4 -  v [2] ] + w [2] )             F[ 4 ] =F[ 2 ]  + w [ 2 ]  =  6

F[ 3 ] = max(F [ 3 ] , F[ 3  -  v [2] ] + w [2] )            F[ 3 ] =F[ 1 ]  + w [ 2 ]  =  6

F[ 2 ] = max(F [ 2 ] , F[ 2  -  v [2] ] + w [2] )            F[ 2 ] =F[ 0 ]  + w [ 2 ]  =  4

 

i = 3 时, j >= v[ 3 ] = 3         w[3] = 4

F[ 5 ] = max(F [ 5 ] , F[ 5  -  v [3] ] + w [3] )           F[ 5 ] =F[ 2 ]  + w [ 3 ]  =  8

F[ 4 ] = max(F [ 4 ] , F[ 4  -  v [3] ] + w [3] )           F[ 4 ] =F[ 1 ]  + w [ 3 ]  =  6

F[ 3 ] = max(F [ 3 ] , F[ 3  -  v [3] ] + w [1] )           F[ 3 ] =F[ 0 ]  + w [ 3 ]  =  4 < 6  则F[ 3 ] = 6

 

i = 4 时, j >= v[4] = 4         w[4] = 5

F[ 5 ] = max(F [ 5 ] , F[ 5  -  v [4] ] + w [4] )        F[ 5 ] =F[ 1 ]  + w [ 4 ]  =  7 < 8 则F[ 5 ] = 8

F[ 4 ] = max(F [ 4 ] , F[ 4  -  v [4] ] + w [4] )        F[ 4 ] =F[ 0 ]  + w [ 4 ]  =  5 < 6 则F[ 4 ] = 6

强调:滚动数组主要体现在层次之间存在依赖关系,01背包要求容量维度要从大到小遍历,否则会出现错乱现象,下面将举例分析

逆序遍历主要保证本层中F[ j - v[ i ]] + w[ i ]   的层数为i - 1

若为正序,例子中会先算出F[ 2 ]此时层数为 i   ,计算F [ 4 ] 时需用到 i  - 1  层的F[ 2 ],两者发生冲突

则应采取逆序遍历。

PS:第一次写背包问题的笔记,语言组织一般,哪里若有错误,望聚聚指正。

 

posted @ 2021-07-05 11:13  cust-bbtking  阅读(79)  评论(0编辑  收藏  举报