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 }
    混合背包
  • 相关阅读:
    Linq聚合操作之Aggregate,Count,Sum,Distinct源码分析
    Linq分区操作之Skip,SkipWhile,Take,TakeWhile源码分析
    Linq生成操作之DefautIfEmpty,Empty,Range,Repeat源码分析
    Linq基础操作之Select,Where,OrderBy,ThenBy源码分析
    PAT 1152 Google Recruitment
    PAT 1092 To Buy or Not to Buy
    PAT 1081 Rational Sum
    PAT 1084 Broken Keyboard
    PAT 1077 Kuchiguse
    PAT 1073 Scientific Notation
  • 原文地址:https://www.cnblogs.com/Misaki-Mei/p/7347556.html
Copyright © 2011-2022 走看看