• 背包问题


    1.   0 1 背包问题:

     1 #pragma GCC optimize("Ofast")
     2 #include <iostream>
     3 #include <algorithm>
     4 #define Maxsize 100 + 1
     5 using namespace std;
     6 typedef long long ll;
     7 struct node{
     8     int v;
     9     int w;
    10 };
    11 node arr[Maxsize];
    12 ll dp[Maxsize][10000 + 1];
    13 int main(){
    14     ios :: sync_with_stdio(false);
    15     cin.tie(0);  cout.tie(0);
    16     int n,capa;
    17     cin >> n >> capa;
    18     for(int i = 1; i <= n; i++)
    19         cin >> arr[i].w >> arr[i].v;
    20 
    21     for(int i = 1; i <= n; i++){
    22         for(int j = 1; j <= capa; j++){
    23             if(arr[i].w <= j)dp[i][j] = max(dp[i-1][j],dp[i-1][j-arr[i].w] + arr[i].v);
    24             else dp[i][j] = dp[i-1][j];
    25             cout<<dp[i][j]<<" ";
    26         }
    27         cout<<endl;
    28     }
    29     cout<<dp[n][capa];
    30     return 0;
    31 }
     1  * Input:
     2  * 3 6
     3  * 2 5
     4  * 3 8
     5  * 4 9
     6  *
     7  * Path:
     8  * 0 5 5 5 5 5
     9  * 0 5 8 8 13 13
    10  * 0 5 8 9 13 14
    11  *
    12  * Output:
    13  * 14

     *

     * 从填写dp数组的顺序可以看出, 每一次新填写的数组单元 , 只会利用到其左上方的单元所保存的结果

     * 转化到实际问题中理解就是 : 当枚举到 前 i 个物品, 容量为 j 的状态时, 只会用到前 i - 1的状态和 前 j - wi 的状态

     * 而不会再用到 i-2 、i-3 的状态了, 同理, 下一次枚举 i + 1 个物品时, 也不会再用 i - 1 状态的结果

     * 也就是说,0 1 背包问题的每一个状态, 只与最近一次的外层循环的状态有关.

     * 因此可以利用滚动数组优化空间复杂度

     * 外层 for 循环 i 为 1 ~ n

     * 内层 for 循环 j 为 V ~ 1

     * 为什么 j 不是从 1 ~ V 呢 ?

     * 假如 j 从 1 ~ V,举个例子:

     * 我们先确定了 状态 dp[j-1] , 现在正在确定 dp[j]

     * 我们在一维数组中,是写成 dp[i][j] = dp[i-1][j]  或 dp[i-1][j-wi] + vi

     * 表示的是 , dp[i][j] 是通过 前 i-1个物品的状态推导得到的

     * 然而我们的 1维dp,会通过用 i 来覆盖 i-1状态, 假如正着遍历 j , 那么会首先将 [i-1][....] 的状态先覆盖成为 [i][...], 再用覆盖后的状态来得到 dp[i][j]

     * 那么就会导致原本的 dp[i-1][j-wi] + vi 变成了 dp[i][j-wi] , 也就是通过 i 个物品, j-wi 个容量推导得到了 前 i 个物品, j 个容量的状态

     * 这显然是错误的.

    用滚动数组优化空间复杂度:

     1 #pragma GCC optimize("Ofast")
     2 #include <iostream>
     3 using namespace std;
     4 struct node{
     5     int w;
     6     int v;
     7 };
     8 node arr[101];
     9 int dp[1000];
    10 int main(){
    11     int n,V;
    12     cin >> n >> V;
    13     for(int i = 1; i <= n; i++)
    14         cin >> arr[i].w >> arr[i].v;
    15     
    16     for(int i = 1; i <= n; i++)
    17         for(int j = V; j >= arr[i].w; j--)
    18             dp[j] = max(dp[j],dp[j-arr[i].w] + arr[i].v);
    19     return 0;
    20 }

    2. 完全背包问题:

    考虑到在对 0 1 背包用滚动数组优化的时候,之所以要求容量 j 必须倒着遍历,就是为了保证在枚举状态 dp[i][j] 时所使用的状态是还没有被 dp[i][j-?] 覆盖的状态(也即dp[i-1][j-??])

    而完全背包恰好就要求每一个物品可以多次使用。 那么,在推导 dp[i][j] 时, 其最优解可能就是由 dp[i][j-??] 推导得到的。

    所以,解完全背包问题,只需要将01背包的滚动数组优化的解法中j正着遍历就可以了。

     1 完全背包
     2 #pragma GCC optimize("Ofast")
     3 #include <iostream>
     4 using namespace std;
     5 struct node{
     6     int w;
     7     int v;
     8 };
     9 node arr[100];
    10 int dp[10000];  // 不少于最大容量
    11 int main(){
    12     int n,V;
    13     cin >> n >> V;
    14     for(int i = 1; i <= n; i++)
    15         cin >> arr[i].w >> arr[i].v;
    16     for(int i = 1; i <= n; i++)
    17          for(int j = arr[i].w; j <= n; j++)
    18                  dp[j] = max(dp[j],dp[j-arr[i].w] + arr[i].v);
    19 
    20      cout<<dp[V];
    21      return 0;
    22 }

    3.多重背包: 三种解法 , 单调队列的解法最优 , 不过还没完全弄懂 , 等以后弄懂了再写详细注释。现在先记模版。

      1 /*
      2  * 多重背包问题的三种解法
      3  *
      4  */
      5 #include <iostream>
      6 #include <vector>
      7 #include <queue>
      8 #define Maxsize 100
      9 using namespace std;
     10 void solution1(){  // 最朴素、无优化
     11     struct node{
     12         int w;
     13         int v;
     14         int num;
     15     };
     16     int n,v;
     17     node arr[Maxsize];
     18     int dp[Maxsize];
     19     cin >> n >> v;
     20     for(int i = 1; i <= n; i++)
     21         cin >> arr[i].w >> arr[i].v >> arr[i].num;
     22     
     23     for(int i = 1; i <= n; i++){
     24         for(int j = 1; j <= arr[i].num; j++){
     25             for(int p = v; p >= j*arr[i].w; p--){
     26                 dp[p] = max(dp[p],dp[p-j*arr[i].w]+j*arr[i].v);
     27             }
     28         }
     29     }
     30     
     31     // 状态转移方程
     32     // dp[i][j] = max(dp[i-1][j-k*arr[i].w] + k*arr[i].v)    { 其中 0 <= k <= arr[i].num 且  k*arr[i].w <= p }
     33     cout<<dp[v];
     34 }
     35 void solution2(){ // 二进制优化
     36     struct node{
     37         int w;
     38         int v;
     39     };
     40     int n,v;
     41     vector<node> vec;
     42     int dp[Maxsize];
     43     cin >> n >> v;
     44     int now_w,now_v,now_num;
     45     for(int i = 0; i < n; i++){
     46         int base = 1;
     47         cin >> now_w >> now_v >> now_num;
     48         int sum = 0;
     49         while(base * now_w <= v && sum + base <= now_num){
     50             node New;   New.w = base * now_w;   New.v = base * now_v;
     51             vec.push_back(New);
     52             sum += base;
     53             base <<= 1;
     54         }
     55         if(sum != now_num && (now_num - sum)*now_w <= v){
     56             node New;   New.w = (now_num - sum) * now_w;   New.v = (now_num - sum) * now_v;
     57             vec.push_back(New);
     58         }
     59     }
     60     
     61     int s = (int)vec.size();
     62     for(int i = 0; i < s; i++){ // 注意从0开始读入
     63         for(int j = v; j >= vec[i].w; j--){
     64             dp[j] = max(dp[j],dp[j-vec[i].w] + vec[i].v);
     65         }
     66     }
     67     cout<<dp[v];
     68     // 状态转移方程
     69     // dp[j] = max(dp[j],dp[j-k*arr[i].w] + k*arr[i].v)    { 其中 0 <= k <= arr[i].num 且  k*arr[i].w <= p }
     70 }
     71 void solution3(){
     72     struct node{
     73         int f;
     74         int id;
     75     };
     76     int dp[20000];
     77     int n,v;
     78     cin >> n >> v;
     79     int now_w,now_v,now_num;
     80     for(int i = 1; i <= n; i++){  // 枚举物品
     81         cin >> now_w >> now_v >> now_num;
     82         now_num = min(now_num,v/now_w); // 这个物品所能选择的数量 , 需满足不大于背包体积 , 这个 now_num 就是我们的区间大小
     83         for(int j = 0; j < now_w; j++){ // 枚举当前余数
     84             deque<node> dq;
     85              // 由solution2 可以知道, dp[j] 的子问题只包含了 dp[j-k*arr[i].w] , 也就是说,它们是关于 arr[i].w 同余的
     86              // 将各个状态按照对 w 取模所得余数进行分类
     87              // 那么只有同一类的状态可以相互影响, 不同类的状态无法相互影响
     88             for(int k = 0; k * now_w + j <= v; k++){
     89                 node New;
     90                 New.f = dp[k*now_w+j] - k*now_v;
     91                 New.id = k;
     92                 while(not dq.empty() and dq.back().f < New.f)
     93                     dq.pop_back();
     94                 
     95                 dq.push_back(New);
     96                 dp[k*now_w+j] = dq.front().f + k*now_v;
     97                 if(not dq.empty() and New.id -dq.front().id == now_num)
     98                     dq.pop_front();
     99             }
    100         }
    101     }
    102     cout<<dp[v];
    103 }
    104 int main(){
    105     solution3();
    106     return 0;
    107 }

     

    ---- suffer now and live the rest of your life as a champion ----
  • 相关阅读:
    BEA WebLogic JRockit的使用和性能调优
    项目调优笔记
    设计资源收集
    2011年推荐WEB在线应用
    Oracle10g调优:Oracle10g AWR使用方法及分析
    谈数据库的性能优化
    EXTJS双击关闭标签代码
    chrome插件API协议(Extensions Context Menu API Proposal)
    ASH Report For AWDDB/awddb
    java内存查看与分析
  • 原文地址:https://www.cnblogs.com/popodynasty/p/12197225.html
Copyright © 2020-2023  润新知