二进制优化

宝物筛选

题目描述

终于,破解了千年的难题。小 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;
}
posted @ 2024-04-11 21:39  Nijika  阅读(14)  评论(0)    收藏  举报