• 2. 01背包问题


    有 NN 件物品和一个容量是 VV 的背包。每件物品只能使用一次。

    第 ii 件物品的体积是 vivi,价值是 wiwi。

    求解将哪些物品装入背包,可使这些物品的总体积不超过背包容量,且总价值最大。
    输出最大价值。

    输入格式

    第一行两个整数,NVN,V,用空格隔开,分别表示物品数量和背包容积。

    接下来有 NN 行,每行两个整数 vi,wivi,wi,用空格隔开,分别表示第 ii 件物品的体积和价值。

    输出格式

    输出一个整数,表示最大价值。

    数据范围

    0<N,V10000<N,V≤1000
    0<vi,wi10000<vi,wi≤1000

    输入样例

    4 5
    1 2
    2 4
    3 4
    4 5
    

    输出样例:

    8


    第一种动态规划二维版本:
    思路:
    闫式DP分析法:
    划分为两部分:
    第一部分:状态表示 f[i][j]:
    (又分为两步--->)
    (1)集合:所有的选法和选取条件---->本题只从i个物品中选取,和物品总体积小于等于j
    (2)属性分为--->最大值,最小值(这个二维有时候需要考虑边界问题,初始化个正无穷就好),数量
    第二部分是状态计算------>集合的划分:

    本题难点

    分不含i和含i----->不含i用f[i - 1][j],含i用f[i - 1,(j - v[i]) + w[i]]表示
     1 #include<iostream>
     2 #include<algorithm>
     3 
     4 using namespace std;
     5 
     6 const int N = 1010;
     7 
     8 int n,m;
     9 int v[N],w[N];
    10 int f[N][N];
    11 int main(){
    12     cin >> n >> m;
    13     for(int i = 1;i <= n;i++) cin >> v[i] >> w[i];
    14     
    15     //物品的数量和背包体积
    16     for(int i = 1;i <= n;i++)
    17         for(int j = 0;j <= m;j++)
    18         {
    19             //不含第i个物品,和含有第i个物品,不含第i个物品一定存在,但是含有第i个物品不一定存在
    20             //因为我们加上第i个物品的体积可能会大于我们的背包体积(j < vi),所以右边可能会是空集
    21             //第i-1个个物品一定存在,先存到i里边
    22             f[i][j] = f[i - 1][j];
    23             //在原有i-1这个物品的数量体积上,在进行判断是否能在装有第i-1物品的体积基础上用我们的第i个物品的体积进行替换
    24             //使我们背包的利用率达到最大,也就是进行判断在数量为i的物品背包容量不超过j的情况下,能装的物品的最大价值
    25             //是多少
    26             //背包体积不超过j的情况下,用上一个价值去和当前价值进行比较
    27             //f[i][j] 表示的就是我们背包当前的状态表示
    28             if(j >= v[i]) f[i][j] = max(f[i][j],f[i - 1][j - v[i]] + w[i]);
    29         }
    30     cout << f[n][m];
    31     
    32     
    33 }

     接下来是很秀的转化为一维的操作:

    由转状态转移方程----->

    不含i用f[i - 1][j],含i用f[i - 1,(j - v[i]) + w[i]]
    第一个优化:
    我们的f[i]只用到了f[i - 1]这一层,f[i-2],f[i-3]....等层数都没有被用到,所以可以用滚动数组来做。
    第二个优化:
    不管我们用到的哪个j都是<=j的,没有在j的两侧,所以就可以改为一维数组来算

    具体改法:

     1 //转化
     2 #include<iostream>
     3 #include<algorithm>
     4 
     5 using namespace std;
     6 
     7 const int N = 1010;
     8 
     9 int n,m;
    10 int v[N],w[N];
    11 int f[N];
    12 int main(){
    13     cin >> n >> m;
    14     for(int i = 1;i <= n;i++) cin >> v[i] >> w[i];
    15     
    16     //物品的数量和背包体积
    17     for(int i = 1;i <= n;i++)
    18         for(int j = m;j >= v[i];j--)
    19         {
    20             //对代码做等价变形去掉f[i][j] = f[i-1][j]
    21             //j从0 ~ v[i-1]没有意义,所以说j从v[i]开始就可以了
    22             //由于j >= j - v[i]  又因为j 是从小到大枚举
    23             //所以说f[j - v[i]]在第i层被计算过了(也就是说原来的i-1就是现在的i,因为从小到大所以小的就已经被算过了)
    24             //所以说f[j - v[i]]其实是第二层的j - v[i],也就是等价于原先二维的
    25             //max(f[i][j],f[i][j - v[i]]);这样就和原来的f[i - 1][j - v[i]]不一样了
    26             
    27             //解决问题的方案:只需要把j的循环变成从大到小就可以了
    28             //这样的话我们去算f[j]的时候,由于j - v[i] <= j,又因为我们从大到小枚举所有的体积
    29             //所以我们在算f[j]的时候,f[j - v[i]]还没有被更新过,那么他存地就是第i - 1层的v[i]
    30             //那么就等价于f[i - 1][j - v[i]],,,,这样就对了
    31             f[j] = max(f[j],f[j - v[i]] + w[i]);
    32         }
    33     cout << f[m];
    34 }







  • 相关阅读:
    ASP.Net请求处理机制初步探索之旅
    WebService如何调试及测试工具
    winform窗体间传值
    C# 窗体间传值方法大汇总
    c#写windows服务
    C# 公关类(全)
    简单的yoman generator
    Service Worker + Push API + Notification API实现桌面消息推送
    Service Worker
    HTML5桌面通知Notification
  • 原文地址:https://www.cnblogs.com/luyuan-chen/p/11615687.html
Copyright © 2020-2023  润新知