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

浙公网安备 33010602011771号