宝物筛选
题目描述
终于,破解了千年的难题。小 FF 找到了王室的宝物室,里面堆满了无数价值连城的宝物。
这下小 FF 可发财了,嘎嘎。但是这里的宝物实在是太多了,小 FF 的采集车似乎装不下那么多宝物。看来小 FF 只能含泪舍弃其中的一部分宝物了。
小 FF 对洞穴里的宝物进行了整理,他发现每样宝物都有一件或者多件。他粗略估算了下每样宝物的价值,之后开始了宝物筛选工作:小 FF 有一个最大载重为 WWW 的采集车,洞穴里总共有 nnn 种宝物,每种宝物的价值为 viv_ivi,重量为 wiw_iwi,每种宝物有 mim_imi 件。小 FF 希望在采集车不超载的前提下,选择一些宝物装进采集车,使得它们的价值和最大。
输入格式
第一行为一个整数 nnn 和 WWW,分别表示宝物种数和采集车的最大载重。
接下来 nnn 行每行三个整数 vi,wi,miv_i,w_i,m_ivi,wi,mi。
输出格式
输出仅一个整数,表示在采集车不超载的情况下收集的宝物的最大价值。
样例 #1
样例输入 #1
4 20
3 9 3
5 9 1
9 4 2
8 1 3
样例输出 #1
47
提示
对于 30%30\%30% 的数据,n≤∑mi≤104n\leq \sum m_i\leq 10^4n≤∑mi≤104,0≤W≤1030\le W\leq 10^30≤W≤103。
对于 100%100\%100% 的数据,n≤∑mi≤105n\leq \sum m_i \leq 10^5n≤∑mi≤105,0≤W≤4×1040\le W\leq 4\times 10^40≤W≤4×104,1≤n≤1001\leq n\le 1001≤n≤100。
题解
第一眼
背包模版,秒了。
恭喜恭喜, 60pts60pts60pts
好好好,不水行了吧
二进制优化
我们可以将一个数kkk拆分为几个类似于2n2^n2n的数相加,如19=24+319 = 2^4 + 319=24+3。那么,我们就可以用枚举出来的二进制方案取0∼mi0 \sim m_i0∼mi, 复杂度仅为O(NW∑logmi)O(NW \sum \log m_i)O(NW∑logmi),并不会超时
二进制优化CodeCodeCode
for(int i = 1; i <= c/*c为物品数量*/; i *= 2) {
c -= i;
item tmp;//item为结构体
tmp.v = a * i;//v是价值,a是单个物品的价值
tmp.w = b * i;//同理
items.push_back(tmp);//items为结构体类型vector
}
if (c) {//若还有剩余
item tmp;
tmp.v = a * c;//注意不是i
tmp.w = b * c;
items.push_back(tmp);
}
接下来就是美妙的模板了
总代码
#include <iostream>
#include <vector>
using namespace std;
struct item {
int v, w;//数量不需要
};
vector<item> items;
int n ,w;
int dp[40010];
int main() {
int n, m;
cin >> n >> m;
int a, b, c;
for (int i = 1; i <= n; i++) {
cin >> a >> b >> c;
for (int j = 1; j <= c; j *= 2) {
c -= j;
item tmp;
tmp.v = a * j;
tmp.w = b * j;
items.push_back(tmp);
}
if (c) {
item tmp;
tmp.v = a * c;
tmp.w = b * c;
items.push_back(tmp);
}
}
for (int i = 0; i < items.size(); i++) {
for (int j = m; j >= items[i].w; j--) {//注意终止点
if (j >= items[i].w) dp[j] = max(dp[j], dp[j - items[i].w] + items[i].v);
}
}
cout << dp[m] << endl;
return 0;
}
暴力环节
为了呼吁暴力出奇迹(明明就是不会), 善良的我所以贴心的附上了暴力方法
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
int main() {
int n, W;
cin >> n >> W;
vector<int> v(n), w(n), m(n);
for (int i = 0; i < n; ++i) {
cin >> v[i] >> w[i] >> m[i];
}
vector<vector<int>> dp(n + 1, vector<int>(W + 1, 0));
// 暴力dp
for (int i = 1; i <= n; ++i) {
for (int j = 0; j <= W; ++j) {
// 不选择第i种宝物时的最大价值
dp[i][j] = dp[i - 1][j];
for (int k = 1; k <= m[i - 1] && k * w[i - 1] <= j; ++k) {
dp[i][j] = max(dp[i][j], dp[i - 1][j - k * w[i - 1]] + k * v[i - 1]);
}
}
}
// 输出结果
cout << dp[n][W] << endl;
return 0;
}
复杂度O(NV⋅∏mi)O(NV \cdot \prod m_i)O(NV⋅∏mi)
∏\prod∏表示连乘
先别走
这里在提供一种(60+40)pts(60 + 40)pts(60+40)pts代码
为什么是(60+40)(60 + 40)(60+40)呢, 606060指暴力dp的60pts60pts60pts
而40pts40pts40pts指下载下来的样例
利用样例下载法,我们可以轻易得到100pts100pts100pts
CodeCodeCode
//到时候补齐因为我下载限额到了...
SorrySorrySorry
992

被折叠的 条评论
为什么被折叠?



