超大多重背包问题

TopCoder SRM 674 div 1 ClassicProblem

Problem Statement

  This task is about a classic problem in computer science: the knapsack problem.

There are n types of items. The types are numbered 0 through n-1, inclusive. For each valid i, you havecnt[i] items of type i, and each of these items has weightw[i] and value v[i]. You are given the vector <int>scnt, w, and v. You are also given an intlimit.

Find a subset of the available items such that:
  • the total weight of the selected items is smaller than or equal to limit
  • the total value of the selected items is as large as possible
Return the total value of the selected items.

Definition

 
Class: ClassicProblem
Method: maximalValue
Parameters: vector <int>, vector <int>, vector <int>, int
Returns: long long
Method signature: long long maximalValue(vector <int> cnt, vector <int> w, vector <int> v, int limit)
(be sure your method is public)

Limits

 
Time limit (s): 3.000
Memory limit (MB): 256

Constraints

- cnt will contain between 1 and 80 elements, inclusive.
- cnt, w and v will contain the same number of elements.
- Each element in cnt will be between 1 and 1,000,000,000, inclusive.
- Each element in w will be between 1 and 80, inclusive.
- Each element in v will be between 1 and 1,000,000,000, inclusive.
- limit will be between 1 and 1,000,000,000, inclusive.

Examples

0)  
 
{100,100}
{2,3}
{3,5}
6
Returns: 10
You have two types of items. Items of type 0 have weight 2 and value 3. Items of type 1 have weight 3 and value 5. You have 100 items of each type. The weight limit is 6. The best solution is to take two items of type 1. The total value will be 5 + 5 = 10.
1)  
 
{100,100}
{2,3}
{3,5}
5
Returns: 8
We have the same items as in Example 0, but now the weight limit is only 5. In this setting the best solution is to take one item of each type. The total value will be 3 + 5 = 8.
2)  
 
{100,102}
{2,3}
{3,5}
1000000000
Returns: 810
Again we have the same 200 items. This time, the weight limit is 10^9 and the optimal solution is to take all 200 items.
3)  
 
{100,100}
{2,3}
{3,5}
1
Returns: 0
We can't take anything.
4)  
 
{1,2,3,4,5,6,7,8}
{4,2,6,7,5,8,3,1}
{3,6,4,1,2,8,5,7}
15
Returns: 73
5)  
 
{1000000000}
{1}
{1000000000}
1000000000
Returns: 1000000000000000000
Note that the answer can be very large.


多重背包问题隶属于dp优化问题,通常来说对于数据量不大的情况下有固定的模板可以拆解为01背包和完全背包问题。但在本题中,limit、value、count的值都很大,常规的枚举必然也不会奏效。
借鉴了别人的代码,将第i种物品拆分为若干件,每件物品的value:2^(k) * value[i]、weight:2^(k) * weight[i]。且 2^(k1) + 2^(k2) + ... + 2^(ki) == count[i]。保证这些系数可以组合成小于等于count[i]的任意值。
将limit也进行二进制拆分,维护一个状态dp[dx][maxs] 当前所处的二进制位为dx,背包容量为maxs时的最大值。
maxs的最大值取 N*2*2*MAX_WEIGHT。因为经过对count的拆解,每个dx下至多有N*2个值。且dx位上的0~maxs受dx-1位上0~maxs/2影响。
状态转移过程为:
从0到30枚举dx
找出limit在dx位的值d,求出dp[cur][i] = dp[pre][min(maxs-1, 2*i+d)];。因为当前维护的是剩余容量,所以当d为1时,剩余容量至少加一。
接下来就是常规的0/1 dp

#include <bits/stdc++.h>
using namespace std;
#define rep(i,a,b) for(int i=(a),__tzg_##i=(b);i<__tzg_##i;++i)
#define urp(i,a,b) for(int i=(a),__tzg_##i=(b);i>=__tzg_##i;--i)
#define rp(i,b) rep(i,0,b)
#define repd(i,a,b) rep(i,a,(b)+1)
#define mst(a,b) memset(a,b,sizeof(a))
#define vrp(it,v) for(auto it(v.begin());(it)!=(v.end());++it)
#define vtr(v) (v).begin(),(v).end()
#define mp(a,b) make_pair(a,b)
#define fi first
#define se second
#define pb(a) push_back(a)
#define _0(x) (!(x))
#define _1(x) (x)
#define bit(x,y) (((x)>>(y))&1)
typedef vector<int> VI;
typedef pair<int,int> PII;
typedef vector<PII> VPII;
typedef long long ll;

struct ClassicProblem {
    const static int M = 31, X = 81*81*2*2;
    VPII s[M];
    ll dp[2][X];
    VI divide(int x) {
        VI r;
        rp(i,M) if ((1<<i) <= x) r.pb(i), x -= 1<<i;
        else break;
        rp(i,M) if (bit(x,i)) r.pb(i);
        return r;
    }
    long long maximalValue(vector <int> cnt, vector <int> w, vector <int> v, int limit) {
        int n = cnt.size();
        rp(i, n) {
            VI r = divide(cnt[i]);
            for (int e : r) s[e].pb(mp(w[i], v[i]));
        }
        int cur = 0, pre = 1-cur;
        mst(dp, 0);
        rp(pos, M) {
            cur = pre;
            pre = 1-pre;
            mst(dp[cur], 0);
            int d = pos?bit(limit, pos-1):0;
            rp(i, X) dp[cur][i] = dp[pre][min(X-1, 2*i+d)];
            for (auto & e : s[pos]) {
                urp(i, X-1, e.fi) dp[cur][i] = max(dp[cur][i], dp[cur][i-e.fi]+(1ll<<pos)*(ll)e.se);
            }
        }
        return dp[cur][bit(limit, M-1)];
    }
};

int main() {
    ClassicProblem *f = new ClassicProblem;
    assert(f->maximalValue({17452733, 484, 607117, 44102867, 80917, 9955379, 154783, 79, 757486,
                           1800008, 31397, 66532052, 5, 8351, 30063, 538787, 66, 993, 28503205, 696813,
                           93438689, 9474478, 14716355, 93438689, 53851, 75482618, 6856, 93438689, 8033317,
                           31, 4, 2, 2957, 68, 536547, 240613, 19, 290847, 57242428, 1722, 19160318, 692,
                           73009, 5, 93438689, 1431, 8982}, {12, 22, 6, 16, 14, 20, 10, 7, 5, 2, 12, 11,
                           11, 10, 9, 5, 8, 3, 1, 18, 9, 7, 22, 21, 19, 12, 15, 20, 11, 17, 7, 14, 11,
                           15, 5, 4, 22, 6, 11, 21, 17, 20, 22, 18, 12, 6, 6}, {977866, 774579, 977866,
                           7, 54, 830, 3, 977866, 977866, 27341, 977866, 290, 9, 445629, 977866, 6, 525083,
                           1, 977866, 41533, 977866, 977866, 977866, 8, 4203, 977866, 62884, 2, 9, 977866,
                           5005, 2, 727, 9952, 977866, 977866, 275, 70, 61, 834, 651, 977866, 977866, 977866,
                           977866, 16488, 977866}, 38504) == 37651752464ll);
    return 0;
}


你可能感兴趣的:(背包问题)