zoukankan      html  css  js  c++  java
  • 01背包&&完全背包_顺序枚举和逆序枚举的问题_一维数组

    逆序枚举和顺序枚举差异主要在一维数组实现的时候出现

    方程: dp[j]=max(dp[j],dp[j-w[i]]+v[i]);

    测试样例:

     3 5

     3 5        2 6          4 10

    逆序结果: 11

    顺序结果: 12

    12这个错误的数据是怎么来的?

    利用check,打印每次枚举后的结果, 代码如下

     1 #include <iostream>
     2 #include <cstring>
     3 #include <cstdio>
     4 using namespace std;
     5 const int M=1e5+10;
     6 int w[M],v[M];
     7 int n,m;
     8 int dp[M];
     9 
    10 int T;
    11 void check(int i,int j) {
    12 
    13     cout<<"容量为"<<j<<"的背包中,"<<"放入第"<<i<<"个物品
    ";
    14     printf("容量: ");
    15     for(int k=1; k<=m; k++) {
    16         cout<<k<<" ";
    17     }
    18     printf("
    价值: ");
    19     for(int k=1; k<=m; k++) {
    20         cout<<dp[k]<<" ";
    21     }
    22     puts("
    
    ");
    23 }
    24 /*
    25 3 5
    26 3 5 2 6 4 10
    27 
    28 */
    29 void f1() {
    30     memset(dp,0,sizeof(dp));
    31     //dp[j]=max(dp[j],dp[j-w[i]]+v[i]);i(1,n),j>=w[i],
    32     //容量初始值j=m
    33     //决策时i为常数, 所以 i 在最外层
    34     for(int i=1; i<=n; i++) {
    35         /*
    36         for(int j=m; j>=w[i]; j--) { //
    37             dp[j]=max(dp[j],dp[j-w[i]]+v[i]);
    38         }*/
    39         for(int j=1; j<=m; j++) {
    40             if(j>=w[i])dp[j]=max(dp[j],dp[j-w[i]]+v[i]);
    41             check(i,j);
    42         }
    43     }
    44     cout<<dp[m]<<endl;
    45 }
    46 void f2() {
    47     memset(dp,0,sizeof(dp));
    48     //dp[j]=max(dp[j],dp[j-w[i]]+v[i]);i(1,n),j>=w[i],
    49     //容量初始值j=m
    50     //决策时i为常数, 所以 i 在最外层
    51     for(int i=1; i<=n; i++) {
    52 
    53         for(int j=m; j>=w[i]; j--) { //
    54             dp[j]=max(dp[j],dp[j-w[i]]+v[i]);
    55             check(i,j);
    56         }
    57 
    58     }
    59     cout<<dp[m]<<endl;
    60 }
    61 /*
    62 5 1000
    63 144 990
    64 487 436
    65 210 673
    66 567 58
    67 1056 897
    68 
    69 2099
    70 */
    71 int main() {
    72 
    73     cin>>n>>m;
    74     for(int i=1; i<=n; i++)cin>>w[i]>>v[i];
    75     f1();
    76     system("pause");
    77     f2();
    78 
    79     return 0;
    80 }
    View Code

     对于放入第2个物品,容量为3的枚举,dp[3]= 6, 6= dp[3-2]+6

     对于放入第2个物品,容量为4的枚举,dp[4]= 12, 12= dp[4-2]+6 

    在第4次枚举的时候发现问题,

    原因在于dp[4]=dp[j]=max(dp[j],dp[j-w[i]]+v[i]);

    dp[4]=dp[2]+6 = 6+6

    然鹅,dp[2]已经更新过,通过装入物品2,

    此时dp[2]的值是容量为2的时候的最大值, 

    在这个过程里, 第2个背包被使用了两次, 

    重复枚举就是利用先更新容量小的背包实现的

    而dp[5]则是直接继承了dp[4]

    每次都利用之前的最大值,并且每个背包都放进去试一试, 可以继承已经更新过的--前驱背包的--"最大价值", 也就可以重复无限次, 

    这样枚举出来的结果是完全背包的最大价值

    ============//=============

    而逆序枚举呢?

     和顺序枚举不一样的地方从放入第2个物品开始,

    逆序 dp[5]=dp[5-2]+6=dp[3]+6, 一样用的是之前更新过的最大值,

    但是区别在于, 之前更新过的dp[3], 并没有被第2个物品放入过, 也就是说没有被枚举更新过, 也就是说不存在重复更新,

    同理, dp[4] =dp[4-2]+6=dp[2]+6;

    此时dp[2]=0, 还没有被更新过,还没有尝试放入第2个物品过,

    不存在:  容量小的背包在容量大的背包被更新之前就更新过

    结论: 顺序枚举是可取无限次物品的结果, 逆序枚举是每种物品只取一次的结果

    老实一点,可爱多了
  • 相关阅读:
    V8 下的垃圾回收机制
    数据库索引原理
    多线程的实现方法
    网元的概念
    Oracle 数据库实现数据合并:merge
    Linux账号管理
    Linux 进程管理 ps、top、pstree命令
    linux OS与SQL修改时区,系统时间
    数据库的几种模式
    linux上限值网速、限值带宽
  • 原文地址:https://www.cnblogs.com/KID-yln/p/12731289.html
Copyright © 2011-2022 走看看