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

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

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

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

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

01 背包

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

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

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

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

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

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

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

完全背包

每件物品可以有无数件

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] 拆成 1,2,4,8,...,n[i]2m1,2,4,8,...,n[i]-2^m ,其中每个数非负。

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

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

posted @ 2020-06-19 10:56  winechord  阅读(149)  评论(0编辑  收藏  举报