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