• 背包九讲_01背包问题


    问题简介:

    有N件物品和一个容量为V的背包。第i件物品的体积是v[i],价值是w[i]。求解在不超过背包容量的情况下价值最大数。

    符号说明:F [ i ] [ j ]表示在已经使用体积 j 且装了 i 件物品时的总价值;

                      v [ i ]  表示第 i 件物品的体积;

                      w [ i ] 表示第 i 件物品的价值;

    基本思路

    明显的动态规划问题:

    即:F [ i ] [ j ] 的状态仅仅与之间的一个状态有关;

    状态方程:

                       1. 选第 i 件物品:F [ i ] [ j ] = F [ i ] [ j ]

                       2. 不选第 i 件物品:F [ i ] [ j ] =  F [ i-1 ] [ j -v [ i ] ] //  关于这个 j - v [ i ] 的说明,就是说在这个时候我不选第 i 件物品,自然这个空间就腾出来了

    状态转移方程:

                       F [ i ] [ j ] = max {  F [ i ] [ j ]  ,F [ i-1 ] [ j -v [ i ] ]  } //  就是这两个方程中选一个大的出来就行

    代码详述(优化之前):

    #include<iostream>
    #include<cstring>
    #include<algorithm>
    using namespace std;
    const int N = 1010;
    int n, m;//有 n 个背包,总体积为 m
    int f[N][N];//存放状态,
    int v[N], w[N];//分别表示第 i 个物品的体积与价值

    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 个背包
    if(j>=v[i])//选第 i个背包之前要做一次特判
    f[i][j] = max(f[i][j],f[i - 1][j - v[i]]+w[i] );//注意使用max函数的时候是使用( ) 不是 { }
    }
    int res = 0;
    for (int j = 1; j <= m; ++j)
    res = max(res, f[n][j]);
    cout << res << endl;
    }
    /*
    搜索,动归问题都是这个状态问题,状态才是根本
    在写状态转移方程的时候:
    1.先找到到底什么才是限制条件,这个里面背包的体积才是限制条件,所以它就是状态转移方程里的那个变量;
    2.找到第 i 个状态和第 i-1 个状态之间的关系啊;(特别注意是否需要预判)
    */

    动态规划时间复杂度分析:

    状态数:二维数组存放状态(因为每个状态都会检索所以N平方)

    分析状态转移方程中对状态的处理,没有循环,即为线性处理,C

    综上:C*N平方

    优化空间复杂度

    代码详述:

    #include<iostream>
    #include<cstring>
    #include<algorithm>
    using namespace std;
    const int N = 1010;
    int n, m;//有 n 个背包,总体积为 m
    int f[N];//存放状态,表示体积为N的时候,价值最大数
    int v[N], w[N];//分别表示第 i 个物品的体积与价值

    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[i],f[j - v[i]]+w[i] );//注意使用max函数的时候是使用( ) 不是 { }
    cout << f[m] << endl;
    }
    /*
    搜索,动归问题都是这个状态问题,状态才是根本
    在写状态转移方程的时候:
    1.先找到到底什么才是限制条件,这个里面背包的体积才是限制条件,所以它就是状态转移方程里的那个变量;
    2.找到第 i 个状态和第 i-1 个状态之间的关系啊;(特别注意是否需要预判)
    */

    关于代码二的思路,下一次训练再写

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

    以上方法的时间和空间复杂度均为O(N*V),其中时间复杂度基本已经不能再优化了,但空间复杂度却可以优化到O(V)。

    先考虑上面讲的基本思路如何实现,肯定是有一个主循环i=1..N,每次算出来二维数组f[i][0..V]的所有值。那么,如果只用一个数组f [0..V],能不能保证第i次循环结束后f[v]中表示的就是我们定义的状态f[i][v]呢?f[i][v]是由f[i-1][v]和f[i-1] [v-c[i]]两个子问题递推而来,能否保证在推f[i][v]时(也即在第i次主循环中推f[v]时)能够得到f[i-1][v]和f[i-1][v -c[i]]的值呢?事实上,这要求在每次主循环中我们以v=V..0的顺序推f[v],这样才能保证推f[v]时f[v-c[i]]保存的是状态f[i -1][v-c[i]]的值。伪代码如下:

    for i=1..N

    for v=V..0

    f[v]=max{f[v],f[v-c[i]]+w[i]};

    其中的f[v]=max{f[v],f[v-c[i]]}一句恰就相当于我们的转移方程f[i][v]=max{f[i-1][v],f[i- 1][v-c[i]]},因为现在的f[v-c[i]]就相当于原来的f[i-1][v-c[i]]。如果将v的循环顺序从上面的逆序改成顺序的话,那么则成了f[i][v]由f[i][v-c[i]]推知,与本题意不符,但它却是另一个重要的背包问题P02最简捷的解决方案,故学习只用一维数组解01背包问题是十分必要的。

  • 相关阅读:
    初步认识三层架构
    机器学习技法总结(六)Decision Tree Hypothesis
    spring 事件模式 源代码导读
    Missing &#39;name&#39; key attribute on element activity at AndroidMan
    【iOS】KVC 与 KVO
    可用谷歌ip地址
    android menu事件
    #103. 子串查找
    P2590 [ZJOI2008]树的统计
    带修改莫队算法
  • 原文地址:https://www.cnblogs.com/WAsbry/p/12639994.html
Copyright © 2020-2023  润新知