• 4.多重背包问题 II


     当尝试用和完全背包问题相似的思路试图来优化的时候,就会发现优化不了

     用二进制优化

    假设我们这件物品一共有1023个

    我们真的需要从0到1到2一直枚举到1023吗

    有没有一种更高效的方式来枚举呢

    可以的

    我们把若干个第i件物品打包在一起

    打包成2的整次幂的形式

    比如说10组

    1, 2, 4, 8, ...,512

    把第i个物品打包成10组

    每一组最多只能选1次

    2 ^ 10 = 1024

    那么我们是否能用这10组拼凑出来从0到1023中的任何一个数呢

    只用前1组,可以拼出0 ~ 1

    只用前2组,可以拼出0 ~ 3

    只用前3组,可以拼出0 ~ 7

    只用前4组,可以拼出0 ~ 15

    所以用前10组,可以凑出0 ~ 1023

    然后每一个打包起来的第i个物品,可以看成是01背包中的一个物品,因为只可以选1次

    就是说我们用10个新的物品,来表示我们的第i个物品

    然后我们枚举这10个新的物品选或不选,就可以拼凑出来第i个物品的所有方案

    原来需要枚举1024次,现在只需要10次

    就是把1024转化为log 1024 = 10

     所以对于一个一般情况下的s

    k是使得前面所有项相加之和小于等于s的最大的整次幂。

     这个k是最大的一个k使得1+2+4+8+...+2^k <= s

    所以如果给我们第i个物品的数量是s的话

    那么我们就可以把它拆成log s(向下取整)个组新的物品

    新的物品每个最多只能用一次

    所以我们先把所有的物品拆分,再对所有新出来的问题做一遍01背包

    原来的三层循环时间复杂度是 n * v * s

    现在是n * log s个物品的01背包问题

    等于 n * log s * v = n * v * log s

    所以对于本题就是1000 * 2000 * log 2000 = 2 * 10^6 * 11 = 2 * 10 ^ 7

     1 #include <bits/stdc++.h>
     2 using namespace std;
     3 const int N = 11010;
     4 //物品个数最多是1000 * log 2000
     5 //log上取整 
     6 int v[N], w[N];
     7 int dp[N];
     8 int main() {
     9     int n, m;
    10     cin >> n >> m;
    11     int cnt = 0; //新的物品的编号 
    12     for (int i = 1; i <= n; i++) {
    13         int a, b, s;
    14         //读入当前物品的体积,价值,数量 
    15         cin >> a >> b >> s;
    16         int k = 1; //从1开始分 
    17         while (k <= s) { //只要k<=s就可以分,每次把k个第i个物品打包在一起 
    18             cnt++; //编号++ 
    19             v[cnt] = a * k; //新物品的体积 
    20             w[cnt] = b * k; //新物品的价值 
    21             s -= k; // 
    22             k *= 2;
    23         }
    24         if (s > 0) { //这就是剩下的c 
    25             cnt++;
    26             v[cnt] = a * s;
    27             w[cnt] = b * s;
    28         }
    29     }
    30     n = cnt;
    31     for (int i = 1; i <= n; i++) {
    32         for (int j = m; j >= v[i]; j--) {
    33             dp[j] = max(dp[j], dp[j - v[i]] + w[i]);
    34         }
    35     }
    36     cout << dp[m] << endl;
    37     return 0;
    38 }
  • 相关阅读:
    771. Jewels and Stones
    706. Design HashMap
    811. Subdomain Visit Count
    733. Flood Fill
    117. Populating Next Right Pointers in Each Node II
    250. Count Univalue Subtrees
    94. Binary Tree Inorder Traversal
    116. Populating Next Right Pointers in Each Node
    285. Inorder Successor in BST
    292. Nim Game Java Solutin
  • 原文地址:https://www.cnblogs.com/fx1998/p/12832890.html
Copyright © 2020-2023  润新知