超大多重背包问题

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;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值