Luogu P11297 [NOISG2018 Prelim] Knapsack

思路

这道题,乍一看其实就是多重背包的板子题 (实际上就是),只需要普通的二进制优化再加上卡亿点点常就可以了。

那么二进制优化是什么呢,简单来说就是通过把本题中的 \(k_i\) 通过类似二进制表示的方法拆成几个和为 \(k_i\) 的数,从而将其转换为 01 背包。

举个例子,以 12 为例,我们可以将其表示为 \(12=1+2+4+5\),或者说是 \(12=2^0+2^1+2^2+5\),这样就可以把 12 个单独的物品分成了4 件不同的价值与体积为原来数倍的物品,每个物品仅有一件,这样就能将其转换成 01 背包了。

代码

#include<stdio.h>
#define ll long long
#define endl '\n'
#define max(a,b) ((a)>(b)?(a):(b))
#define N 10000005
using namespace std;
int n,m;
ll tot;
ll v[N],w[N],dp[N];
inline ll read(){
	ll f=1,x=0;
	char ch=getchar();
	while(ch<'0'||ch>'9'){
		if(ch=='-')f=-1;
		ch=getchar();
	}
	while(ch>='0'&&ch<='9'){
		x=(x<<3)+(x<<1)+(ch^48);
		ch=getchar();
	}
	return x*f;
}
inline void write(int x){
	if(x>9)write(x/10);
	putchar(x%10+'0');
}
signed main(){
//	freopen("P11297.in","r",stdin);
//	freopen("mamba.out","w",stdout);
	m=read(),n=read();
	for(register ll i=1;i<=n;i++){
		int a,b;ll c;
		a=read(),b=read(),c=read();
		for(register ll j=1;j<=c;j<<=1){ //倍增拆分物品
			v[++tot]=j*a*1LL;
			w[tot]=j*b*1LL;
			c-=j;
		}
		if(c){ //注意处理剩下的物品
			v[++tot]=c*a;
			w[tot]=c*b;
		}
	}
	for(register ll i=1;i<=tot;i++){
		for(register ll j=m;j>=w[i];j--){
			dp[j]=max(dp[j],dp[j-w[i]]+v[i]); //板子
		}
	}
	write(dp[m]);
	return 0;
}
posted @ 2025-02-12 09:52  iridescent94  阅读(9)  评论(0)    收藏  举报