二进制优化
宝物筛选
题目描述
终于,破解了千年的难题。小 FF 找到了王室的宝物室,里面堆满了无数价值连城的宝物。
这下小 FF 可发财了,嘎嘎。但是这里的宝物实在是太多了,小 FF 的采集车似乎装不下那么多宝物。看来小 FF 只能含泪舍弃其中的一部分宝物了。
小 FF 对洞穴里的宝物进行了整理,他发现每样宝物都有一件或者多件。他粗略估算了下每样宝物的价值,之后开始了宝物筛选工作:小 FF 有一个最大载重为 \(W\) 的采集车,洞穴里总共有 \(n\) 种宝物,每种宝物的价值为 \(v_i\),重量为 \(w_i\),每种宝物有 \(m_i\) 件。小 FF 希望在采集车不超载的前提下,选择一些宝物装进采集车,使得它们的价值和最大。
输入格式
第一行为一个整数 \(n\) 和 \(W\),分别表示宝物种数和采集车的最大载重。
接下来 \(n\) 行每行三个整数 \(v_i,w_i,m_i\)。
输出格式
输出仅一个整数,表示在采集车不超载的情况下收集的宝物的最大价值。
样例 #1
样例输入 #1
4 20
3 9 3
5 9 1
9 4 2
8 1 3
样例输出 #1
47
提示
对于 \(30\%\) 的数据,\(n\leq \sum m_i\leq 10^4\),\(0\le W\leq 10^3\)。
对于 \(100\%\) 的数据,\(n\leq \sum m_i \leq 10^5\),\(0\le W\leq 4\times 10^4\),\(1\leq n\le 100\)。
思路
物品可以一个个枚举,但是时间复杂度较高,可以考虑转换成以二进制形式的物品,例如8为1 2 4 1的物品,二进制对于每一个数都是唯一的,所以枚举二进制的每一位也可以构造出所有的可能
怎样实现呢?我们可以创建一个数组,整数最多也就$2^{32} \(,所以我们开35即可,起始为1,每次\)*2$然后减去当前值,如果减<0,那就记录当前值即可
证明: 当x=0时:只要都不选就可以; 当\(1<=x<=2^{k-1}\)时:把x转为二进制,选择对应位为1的物品。如x=5,5的二进制为101,5=1+4; 当\(2^k<=x<=m[i]\)时:由于\(m[i]-(2^k-1)<2^k\),则选择\(m[i]-(2^k-1)\),剩下的为\(x-m[i]+2^k-1<=2^k-1\)可以由前面的1,2,...,\(2^{(k-1)}\)组合得到。
最后枚举物品即可
#include <bits/stdc++.h>
using namespace std;
int main(){
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n,w,vi,wi,mi;
cin >> n >> w;
int dp[40005]={};
for(int i=1;i<=n;i++){
cin >> vi >> wi >> mi;//重量 价值 数量
int s = 1,hel[35]={},t=1;
//初始数为 1
while(mi>0){
if(mi-s>=0){
mi -= s;
hel[t++]=s;
}
else{
hel[t++]=mi;
mi = 0;
}
//构造二进制数组
s *= 2;
}
//hel[l]现在为hel[l]个的数量物品
for(int l=1;l<=t-1;l++){
for(int j=w;j>=wi*hel[l];j--){
//后往前遍历 dp[j]为当前物品容量的最大值
//wi*hel[l]表示物品所占容量
//vi*hel[l]表示物品价值
dp[j] = max(dp[j],dp[j-wi*hel[l]]+vi*hel[l]);
}
}
}
int maxn = 0;
for(int i=w;i>=0;i--){
maxn = max(dp[i],maxn);
}
cout << maxn;
return 0;
}

浙公网安备 33010602011771号