洛谷P1964 【mc生存】卖东西 题解 多重背包

题目链接:https://www.luogu.com.cn/problem/P1964

对于第 \(i\) 件物品:

  • 如果 \(a_i\) 能被 \(c_i\) 整除,则将其转换成 \(\frac{a_i}{c_i}\) 件体积为 \(1\),价格为 \({c_i} \cdot b_i\) 的物品;
  • 如果 \(a_i\) 不能被 \(c_i\) 整除,则将其转换成 \(\lfloor \frac{a_i}{c_i} \rfloor\) 件体积为 \(1\) ,价格为 \({c_i} \cdot b_i\) 的物品 + 一件体积为 \(1\),价格为 (\(a_i\)\(c_i\)\(\cdot b_i\) 的物品。

然后对这些物品进行多重背包即可。

然后这里需要注意的一点是样例中的第 \(1\) 件 和 第 \(3\) 件物品属于同一件物品,所以可以放到同一个格子里。

然后我希望题目的数据能够保证“同一件物品的价值 \(b_i\) 都得一样”,不然解决起来就很麻烦。(AC则保证此条件满足)

实现代码如下:

#include <bits/stdc++.h>
using namespace std;
int v, dp[21];
void zeroonepack(int val,int cost) {
    for(int i=v;i>=cost;i--)
        if(dp[i-cost]+val>dp[i])
            dp[i]=dp[i-cost]+val;
}
void completepack(int val,int cost) {
    for(int i=cost;i<=v;i++)
        dp[i]=max(dp[i], dp[i-cost]+val);
}
void multipack(int val,int cost,int num) {
    if(num*cost>=v) completepack(val,cost);
    else {
        int k=1;
        while(k<num) {
            zeroonepack(k*val, k*cost);
            num-=k;k+=k;
        }
        zeroonepack(num*val, num*cost);
    }
}
int m, n;
struct Node {
    int a, b, c;
    Node () {};
    Node (int _a, int _b, int _c) { a = _a; b = _b; c = _c; }
};
map<string, Node > mp;
int main() {
    cin >> m >> n;
    if (m >= 21) {
        cout << 0 << endl;
        return 0;
    }
    v = 21 - m;
    while (n --) {
        int a, b, c;
        string s;
        cin >> a >> b >> c >> s;
        Node nd;
        if (mp.find(s) != mp.end()) {
            nd = mp[s];
            nd.a += a;
        }
        else nd = Node(a, b, c);
        mp[s] = nd;
    }
    for (map<string, Node>::iterator it = mp.begin(); it != mp.end(); it ++) {
        Node nd = (*it).second;
        int a = nd.a, b = nd.b, c = nd.c;
        if (a%c==0) multipack(b*c, 1, a/c);
        else {
            if (a > c) multipack(b*c, 1, a/c);
            zeroonepack(b*(a%c), 1);
        }
    }
    cout << dp[v] << endl;
    return 0;
}
posted @ 2020-02-17 10:28  quanjun  阅读(247)  评论(0编辑  收藏  举报