zoukankan      html  css  js  c++  java
  • DP——背包问题(一)

                                  以前不是很重视 DP ,遇到 DP 就写贪心、暴搜……其实这是非常错误的,现在开始练习 DP 了才发现,我好菜……

                                  对于DP的整理,先从众所周知的背包问题开始。

    ———————— 01背包:n 个物品,重量和价值分别为 w[i]、v[i],背包容量 W,求所有挑选方案中价值总和的最大值。

                                 DP 方程 :dp[j] = max ( dp[j] , dp[ j - w[i] ] + v[i] ) ,其中 dp[j]为使用 j 的容量获得的最大价值,i 为第 i 件物品。

                                 代码:

     1 #include<cstdio>
     2 #include<cstring>
     3 #include<iostream>
     4 #include<algorithm>
     5 #include<cmath>
     6 #include<string>
     7 using namespace std;
     8 int n,W,w[2000],v[2000],dp[2000];
     9 int main(){
    10     scanf("%d %d",&n,&W);
    11     for(int i=1;i<=n;i++) scanf("%d %d",&w[i],&v[i]);
    12     for(int i=1;i<=n;i++)
    13     for(int j=W;j>=w[i];j--){
    14         dp[j]=max(dp[j],dp[j-w[i]]+v[i]);
    15     }
    16     printf("%d",dp[W]);
    17     return 0;
    18 }
    01背包

    ———————— 完全背包:n 种物品,数目不限,其他的和 01背包 一 样。

                                 DP 方程 : dp[j] = max ( dp[j] , dp[ j - w[i] + v[i] ) ,是不是感觉和 01背包 一样?其实,就是 一样……-- ^ --|||||,但代码有细微差别……

                                 代码:

     1 #include<cstdio>
     2 #include<cstring>
     3 #include<iostream>
     4 #include<algorithm>
     5 #include<cmath>
     6 #include<string>
     7 using namespace std;
     8 int n,W,w[2000],v[2000],dp[2000];
     9 int main(){
    10     scanf("%d %d",&n,&W);
    11     for(int i=1;i<=n;i++) scanf("%d %d",&w[i],&v[i]);
    12     for(int i=1;i<=n;i++)
    13     for(int j=w[i];j<=W;j++){
    14         dp[j]=max(dp[j],dp[j-w[i]]+v[i]);
    15     }
    16     printf("%d",dp[W]);
    17     return 0;
    18 }
    完全背包


                                那么,问题来了,为什么 01背包 是倒着 循环,而 完全背包 是正着循环……动笔模拟一下,你会发现且理解 — 正着循环会对一件物品重复选取╮(╯_╰)╭,而倒着循环就不会……

    ———————— 多重背包:n 种物品,给定数目 m[i],其他和 01 背包一样。

                                 DP 方程:dp[j] =max ( dp[j],dp[ j - w[i] ] + v[i] ),&%&……¥怎么又一样,其实 多重背包 就是特殊处理化的 01背包,这个特殊处理就是 二进制拆分。

                                 二进制拆分:众所周知什么是二进制,二进制拆分就是把 m[i] 个物品拆成 1个物品、2个物品组成的新物品、4个物品组成的新物品、8个物品组成的新物品……一                              直到不能拆为止,因为二进制可以 表示 出任何实数的嘛。

                                 代码:

     1 #include<cstdio>
     2 #include<cstring>
     3 #include<iostream>
     4 #include<algorithm>
     5 #include<cmath>
     6 #include<string>
     7 using namespace std;
     8 int n,W,w[2000],v[2000],dp[2000],m[2000];
     9 int main(){
    10     scanf("%d %d",&n,&W);
    11     int x=n;
    12     int y;
    13     for(int i=1;i<=n;i++) scanf("%d %d %d",&w[i],&v[i],&m[i]);
    14     for(int i=1;i<=x;i++){
    15         y=1;
    16         while(m[i]>y&&m[i]>1){
    17             n++;
    18             w[n]=y*w[i];
    19             v[n]=y*v[i];
    20             m[i]-=y;
    21             y=y*2;
    22         }
    23         w[i]=m[i]*w[i];
    24         v[i]=m[i]*v[i];
    25     }
    26     for(int i=1;i<=n;i++)
    27     for(int j=W;j>=w[i];j--){
    28         dp[j]=max(dp[j],dp[j-w[i]]+v[i]);
    29     }
    30     printf("%d",dp[W]);
    31     return 0;
    32 }
    多重背包

    ———————— 三种混合背包(01背包、完全背包、多重背包):这种问题就是给定 m[i],若 m[i] 为 -1,则为数目无限,否则数目有限,其他的和 01背包 一样。虽然写着是三种                              混合背包,但我认为实际上是两种背包,多重背包 和 完全背包。方法就是能拆分的就拆分,在循环的时候,判断一下,如果是 完全背包 就正着循环,不然就倒                                着循环…………蛮easy的QAQ~

                                 代码:

     1 #include<cstdio>
     2 #include<cstring>
     3 #include<iostream>
     4 #include<algorithm>
     5 #include<cmath>
     6 #include<string>
     7 using namespace std;
     8 int n,W,w[2000],v[2000],dp[2000],m[2000];
     9 int main(){
    10     scanf("%d %d",&n,&W);
    11     int x=n;
    12     int y;
    13     for(int i=1;i<=n;i++) scanf("%d %d %d",&w[i],&v[i],&m[i]);
    14     for(int i=1;i<=x;i++) if(m[i]!=-1) { 
    15         y=1;
    16         while(m[i]>y&&m[i]>1){
    17             n++;
    18             w[n]=y*w[i];
    19             v[n]=y*v[i];
    20             m[i]-=y;
    21             y=y*2;
    22         }
    23         w[i]=m[i]*w[i];
    24         v[i]=m[i]*v[i];
    25     }
    26     for(int i=1;i<=n;i++) 
    27         if(m[i]!=-1)
    28         for(int j=W;j>=w[i];j--){
    29             dp[j]=max(dp[j],dp[j-w[i]]+v[i]);
    30     }
    31     else 
    32         for(int j=w[i];j<=W;j++)
    33              dp[j]=max(dp[j],dp[j-w[i]]+v[i]);
    34     printf("%d",dp[W]);
    35     return 0;
    36 }
    混合背包
  • 相关阅读:
    Scala泛型
    Tensorflow激活函数
    20181030-4 每周例行报告
    20181023-3 每周例行报告
    20181016-10 每周例行报告
    20181009-9 每周例行报告
    第三周作业(4)——单元测试
    第三周作业(5)——代码规范
    第三周作业(2)——功能测试
    第三周作业(3)——词频统计--效能分析
  • 原文地址:https://www.cnblogs.com/Misaki-Mei/p/7347556.html
Copyright © 2011-2022 走看看