多重背包的拆分问题

多重背包应该算是背包问题里面比较麻烦的一种了,基本思路就是拆分转化为01背包,进而套01背包的模版

今天在洛谷刷题的时候遇到一道题,是一个多重背包和完全背包的组合型问题

题目描述

爱与愁大神后院里种了n棵樱花树,每棵都有美学值Ci。爱与愁大神在每天上学前都会来赏花。爱与愁大神可是生物学霸,他懂得如何欣赏樱花:一种樱花树看一遍过,一种樱花树最多看Ai遍,一种樱花树可以看无数遍。但是看每棵樱花树都有一定的时间Ti。爱与愁大神离去上学的时间只剩下一小会儿了。求解看哪几棵樱花树能使美学值最高且爱与愁大神能准时(或提早)去上学。

输入

共n+1行:

第1行:三个数:现在时间Ts(几点:几分),去上学的时间Te(几点:几分),爱与愁大神院子里有几棵樱花树n。

第2行~第n+1行:每行三个数:看完第i棵树的耗费时间Ti,第i棵树的美学值Ci,看第i棵树的次数Pi(Pi=0表示无数次,Pi是其他数字表示最多可看的次数Pi)。

输出

只有一个整数,表示最大美学值。

样例输入
6:50 7:00 3
2 1 0
3 3 1
4 5 4
样例输出
11

这道题我一开始的思路是用时间的限制求出完全背包的上限,然后将完全背包转换为多重背包,进而进行拆分,就可以套01背包的板子

这是一个我作为一名蒟弱 的的第一反应

交完之后很尴尬的,除了第一个点都TLE了,后来发现这道题在拆分的时候要用到2进制拆分

二进制拆分:

方法:

将一个数字用根据2的多少次方进行拆分,如:

7=1+2+4;

9=1+2+4+2;

这样做的好处就是减少了占用的空间,并且将拆分数字组合相加可以得到(1~n)之间的任何一个数字,并且组合方式较少

而如果采用拆分成1的话,就会产生较多的组合方式,造成了重复运算

最后,上面那道题的代码

#include <iostream>
#include <stdio.h>
using namespace std;
int sumt,num,x,y,z,T[100005],M[100005],ans[10001];
int main(){
    int h1,h2,m1,m2,n;
    scanf("%d:%d %d:%d %d",&h1,&m1,&h2,&m2,&n);
    sumt=h2*60+m2-h1*60-m1;
    for (int i = 0; i < n; ++i)
    {
        cin>>x>>y>>z;
        if(z==0)
            z=sumt/x+1;
        int t=1;
        while(z){
            T[num]=t*x;
            M[num]=t*y;
            z-=t;
            t*=2;
            num++;
            if(z<=t){
                T[num]=z*x;
                M[num]=z*y;
                num++;
                break;
            }
        }
    }
    for (int i = 0; i <= num; ++i)
    {
        for (int j = sumt; j >= T[i]; --j)
        {
            ans[j]=max(ans[j],ans[j-T[i]]+M[i]);
        }
    }
    cout<<ans[sumt]<<endl;

}

猜你喜欢

转载自blog.csdn.net/tlyzxc/article/details/105317883