洛谷p1833-樱花

题目链接:P1833 樱花 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

题干:

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

Input

共 n+1n+1行:

第 11 行:现在时间 T_sTs(几时:几分),去上学的时间 T_eTe(几时:几分),爱与愁大神院子里有几棵樱花树 nn。这里的 T_sTs,T_eTe 格式为:hh:mm,其中 0 \leq hh \leq 230≤hh≤23,0 \leq mm \leq 590≤mm≤59,且 hh,mm,nhh,mm,n 均为正整数。

第 22 行到第 n+1n+1 行,每行三个正整数:看完第 ii 棵树的耗费时间 T_iTi,第 ii 棵树的美学值 C_iCi,看第 ii 棵树的次数 P_iPi(P_i=0Pi=0 表示无数次,P_iPi 是其他数字表示最多可看的次数 P_iPi)。

Output

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

思路:

本蒟蒻是没想出来TAT,看了其他dalao的思路。

大体思路是把各种用二进制拆分,然后就是01背包了。

讲下二进制拆分:

对于存在多个的,都可以用二进制来表示,例如:7可以拆成1、2、4,如果有剩余(不够凑够二进制下一位)的就直接放在下一位就行了。由二进制的数字,我们可以组合到从1到这个数字的所有可能性,所以是一种快捷的遍历方式,例如数字7可以用拆分到1、2、4,由这些二进制数字我们可以组合成1到7的所有可能重量,实现一种较为方便的遍历方式。

代码:

void init(){
    for(int i=1;i<=n;i++){
        int count=1; //计算当前拆分到哪个2的倍数
        while(c[i]){  //直到拆分完结束
            w[++now]=count*a[i];  //当前这份的重量*单个的重量
            v[now]=count*b[i];
            c[i]-=count;  //用完的要减去
            count*=2; 
            if(c[i]<count){  //下一份不够下一个2的倍数的话就直接分配到下一个位置上。
                w[++now]=a[i]*c[i];
                v[now]=b[i]*c[i];
                break;
            }
        }
    }
}

其他就是正常的01背包模板

完整代码:

#include <cstdio>
#include <algorithm>
#include <iostream>
using namespace std;
const int N=1e7+5;
const int M=1e5+5;
int num,w[N],v[N];
int a[M],b[M],c[M],f[N];
int time1,n;
int now=0;
void init(){
    
    for(int i=1;i<=n;i++){
        int count=1;
        while(c[i]){
            w[++now]=count*a[i];
            v[now]=count*b[i];
            c[i]-=count;
            count*=2;
            if(c[i]<count){
                w[++now]=a[i]*c[i];
                v[now]=b[i]*c[i];
                break;
            }
        }
    }
}
int main()
{
    int x1,y1,x2,y2;
    scanf("%d:%d %d:%d %d",&x1,&y1,&x2,&y2,&n);
    //cout<<x1<<y1<<x2<<y2;
    time1=x2*60+y2-(x1*60+y1);
    for(int i=1;i<=n;i++){
        scanf("%d %d %d",&a[i],&b[i],&c[i]);
        if(!c[i]) c[i]=N;
    }
    init();
    for(int i=1;i<=now;i++){
        for(int j=time1;j>=w[i];j--){
            f[j]=max(f[j],f[j-w[i]]+v[i]);
        }
    }
    cout<<f[time1];
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

爱吃芒果的蘑菇

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值