【C++/混合背包】樱花

320 阅读3分钟

题目背景

《爱与愁的故事第四弹·plant》第一章。

题目描述

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

输入格式

共 n+1行:

第 1 行:现在时间 Ts​(几时:几分),去上学的时间 Te​(几时:几分),爱与愁大神院子里有几棵樱花树 n。这里的 Ts​,Te​ 格式为:hh:mm,其中0≤hh≤23,0≤mm≤59,且 hh,mm,n 均为正整数。

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

输出格式

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

输入输出样例

输入 #1

6:50 7:00 3
2 1 0
3 3 1
4 5 4

输出 #1

11

说明/提示

100% 数据:Te​−Ts​≤1000(即开始时间距离结束时间不超过 1000 分钟),n≤10000。保证 Te​,Ts​ 为同一天内的时间。

样例解释:赏第一棵樱花树一次,赏第三棵樱花树 2 次。

思路

涉及总的时间、每次赏花的时间和价值、以及次数。背包问题无疑,与以往的背包问题不一样的是这次是有的可以选择无数次,有的选择有限制次数。所以是个混合背包,混合的是完全背包和多重背包。模板!

混合背包模板

#include<bits/stdc++.h>
using namespace std;
const int INF=0x3f3f3f3f;
int main()
{
    int n,V;
    cin>>n>>V;
    int v,w,p;//v是每一组数据的体积 w是每一组数据的价值 p是物品的使用次数
              //如果p=-1,则是01背包;如果p=0,则是完全背包,如果p大于0,则是多重背包,大于0的时候p代表该物品的使用次数
    for (int i = 0; i <n; i++)
    {
        cin>>v>>w>>p;
        if(p==-1)  //01背包   物品只能用一次
        {
            for(int j=V;j>=v;j--)
            {
                dp[j]=max(dp[j],dp[j-v]+w);
            }
        }
        else if(p==0)  //完全背包   物品可以用无数次
        {
            for(int j=v;j<=V;j++)
            {
                dp[j]=max(dp[j],dp[j-v]+w);
            }
        }
        else  //多重背包   物品有使用次数的限制
        {
            int num=min(p,V/v);
            for(int k=1;num>0;k<<=1)
            {
                if(k>num) k=num;
                num-=k;
                for(int j=V;j>=v*k;j--)
                {
                    dp[j]=max(dp[j],dp[j-v*k]+w*k);
                }
            }
        }

    }
    cout<<dp[V]<<endl;
    return 0;
}

AC代码

#include<bits/stdc++.h>
using namespace std;
const int INF=0x3f3f3f3f;
int main()
{
    int n,V,sth,eth,stm,etm;
    scanf("%d:%d %d:%d %d",&sth,&stm,&eth,&etm,&n);
    int nums=sth*60+stm;
    int nume=eth*60+etm;
    V=nume-nums;  //此题背包的总体积是两个输入时间的差,都转化为分钟相减即可
    
    int v,w,p;  //v是每朵花要的时间 w是每朵花的价值 p是次数限定
                //p=0的时候,无限制次数 p!=0的时候,p为次数上限
    for (int i = 0; i <n; i++)//由于本题不涉及01背包,则可以删除多余的if分支
    {
        cin>>v>>w>>p;
        if(p==0)  //完全背包   物品可以用无数次
        {
            for(int j=v;j<=V;j++)
            {
                dp[j]=max(dp[j],dp[j-v]+w);
            }
        }
        else  //多重背包   物品有使用次数的限制
        {
            int num=min(p,V/v);
            for(int k=1;num>0;k<<=1)
            {
                if(k>num) k=num;
                num-=k;
                for(int j=V;j>=v*k;j--)
                {
                    dp[j]=max(dp[j],dp[j-v*k]+w*k);
                }
            }
        }

    }
    cout<<dp[V]<<endl;
    return 0;
}

//2022.4.20 混合背包模板题