洛谷 P1833 樱花 二进制优化的多重背包

n n n 棵樱花树,不同的树有不同的最多欣赏次数(0 时为无数),看每棵树每次都会花一定的时间,不同的树有不同的美学值,问在给定的时间内,可以得到的最大美学值为多少?

思路:混合背包 / 二进制优化

在过程中判断是 01背包 / 多重背包 / 完全背包

  • 01 背包:每种物品只有一件
  • 完全背包:每种物品无数
  • 多重背包:每种物品有各自的数量限制

01 背包

f [ i ] [ v ] f[i][v] f[i][v] 表示前 i i i 件物品满足背包容量可以达到的最大价值,则

f [ i ] [ v ] = max ⁡ { f [ i − 1 ] [ v ] , f [ i − 1 ] [ v − c [ i ] ] + w [ i ] } f[i][v]=\max\{f[i-1][v],f[i-1][v-c[i]]+w[i]\} f[i][v]=max{f[i1][v],f[i1][vc[i]]+w[i]}

相当于考虑当前第 i i i 物品放还是不放。

for i=1...N
	for v=V...c[i]
		f[v]=max(f[v],f[v-c[i]]+w[i]);

这个是对二维数组的优化,其中 v 是倒序处理,因为更新的时候需要的是上一次状态的值,即 f [ i − 1 ] [ . . . ] f[i-1][...] f[i1][...] ,注意此处写成正序时实际上是完全背包——每个物品有无数件。

当背包必须装满时,初始化 f [ 0 ] = 0 f[0]=0 f[0]=0 其余都为 − ∞ -\infty ,表示没有任何物品在背包时只有容量为 0 0 0 的背包是合法的。

当背包不要求必须装满时,初始化 f f f 都为 0 0 0

完全背包

每件物品可以有无数件

for i=1...N
	for v=c[i]...V
		f[v]=max(f[v],f[v-c[i]]+w[i])

多重背包

物品有 n[i]

for i=1...N
	k=1
	while k<n[i]
		for v=V...k*c[i] // 01 背包
			f[v]=max(f[v],f[v-k*c[i]]+k*w[i])
		n[i]-=k
		k*=2 // 二进制拆分
	for v=V...n[i]*c[i] // 01 背包
		f[v]=max(f[v],f[v-n[i]*c[i]]+n[i]*w[i])

注意这里的二进制拆分,是将一个数 n [ i ] n[i] n[i] 拆成 1 , 2 , 4 , 8 , . . . , n [ i ] − 2 m 1,2,4,8,...,n[i]-2^m 1,2,4,8,...,n[i]2m ,其中每个数非负。

回到题目,完全背包可以看成数量很大的物体:

#include<iostream>
#include<cstdio>
#include<algorithm>
#define MAXN 10010
#define MAXM 1010
using namespace std;
int m1,m2,s1,s2,n,T,t[MAXN],c[MAXN],p[MAXN],f[MAXM];
int main(){
#ifdef WINE
    freopen("data.in","r",stdin);
#endif
    scanf("%d:%d %d:%d%d",&m1,&s1,&m2,&s2,&n);
    T=(m2-m1)*60+(s2-s1);
    for(int i=1;i<=n;i++)
        scanf("%d%d%d",&t[i],&c[i],&p[i]);
    for(int i=1;i<=n;i++){
        if(!p[i])
            for(int v=t[i];v<=T;v++)
                f[v]=max(f[v],f[v-t[i]]+c[i]);
        else{
            int k=1;
            while(k<p[i]){
                for(int v=T;v>=k*t[i];v--)
                    f[v]=max(f[v],f[v-k*t[i]]+k*c[i]);
                p[i]-=k;
                k*=2;
            }
            for(int v=T;v>=p[i]*t[i];v--)
                f[v]=max(f[v],f[v-p[i]*t[i]]+p[i]*c[i]);
        }
    }
    printf("%d\n",f[T]);
    return 0;
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值