【动态规划、dp】P1833 樱花 题解

前言

之前在 CSDN 上看某大佬讲解自己学DP刷了六十几题后完全领悟的故事,深有感触,自己实操了一下,刷了十几题以后就大概知道怎么转移了,这是洛谷比较火的一个动态规划题单链接,这里分享给大家。

题目描述

爱与愁大神后院里种了 n n n 棵樱花树,每棵都有美学值 C i ( 0 ≤ C i ≤ 200 ) C_i(0 \le C_i \le 200) Ci(0Ci200)。爱与愁大神在每天上学前都会来赏花。爱与愁大神可是生物学霸,他懂得如何欣赏樱花:一种樱花树看一遍过,一种樱花树最多看 P i ( 0 ≤ P i ≤ 100 ) P_i(0 \le P_i \le 100) Pi(0Pi100) 遍,一种樱花树可以看无数遍。但是看每棵樱花树都有一定的时间 T i ( 0 ≤ T i ≤ 100 ) T_i(0 \le T_i \le 100) Ti(0Ti100)。爱与愁大神离去上学的时间只剩下一小会儿了。求解看哪几棵樱花树能使美学值最高且爱与愁大神能准时(或提早)去上学。
【数据范围】
100 % 100\% 100% 数据: T e − T s ≤ 1000 T_e-T_s \leq 1000 TeTs1000(即开始时间距离结束时间不超过 1000 1000 1000 分钟), n ≤ 10000 n \leq 10000 n10000。保证 T e , T s T_e,T_s Te,Ts 为同一天内的时间。

在这里插入图片描述

思路

原本思路:

首先对于每一个能看采无限次转化为能看 ⌊ T e − T s T i ⌋ \lfloor \dfrac{T_e-T_s}{T_i} \rfloor TiTeTs,否则较为难以处理。对于每一个能看 P i P_i Pi 次的花转化为 p i p_i pi 个能看 1 1 1 次的花,最后用 01 背包求解。时间复杂度

O ( T ∑ i = 1 c n t p i ) O(T\sum_{i=1}^{cnt}{p_i}) O(Ti=1cntpi)

考虑优化:

对于每一个能看 P i P_i Pi 次的花,我们一定要转化成 p i p_i pi 个吗?

考虑 p i p_i pi 的二进制形式,可以发现可以转化成 1 + 2 + 4 + 8 + ⋯ + 2 p o w + P i − ∑ j = 0 c n t 2 j 1+2+4+8+\dots+2^{pow}+P_i - \sum_{j=0}^{cnt}{2^j} 1+2+4+8++2pow+Pij=0cnt2j 的形式,即复杂度变为

O ( T ∑ i = 1 c n t log ⁡ 2 p i ) O(T\sum_{i=1}^{cnt}{\log_2 p_i}) O(Ti=1cntlog2pi)

代码

#include<bits/stdc++.h>
#define time time_1
using namespace std;
int read(string x) {
	int h = 0,min = 0,siz = x.size();
	int i = 0;
	for(;x[i] != ':';i++) h*=10,h+=int(x[i] - '0');
	i++;
	for(;i < siz;i++) min *= 10,min += int(x[i] - '0');
	return h * 60 + min;
}
int n,t[100005],c[100005],cnt = 0;
int dp[2][100005]; 
string start,to;
int time;
signed main() {
	cin>>start>>to;
	cin>>n;
	time = read(to) - read(start);
	for(int i = 1;i <= n;i++) {
		int t_1,c_1,b;
		cin>>t_1>>c_1>>b;
		if(b == 0) b = time / t_1;
		int po = 1;
		while(b >= po) {
			t[++cnt] = po * t_1;
			c[cnt] = po * c_1;
			b -= po;
			po *= 2;
			//printf("%lld %lld\n",b,po);
		}
		if(b) {
			t[++cnt] = b * t_1;
			c[cnt] = b * c_1;
		}
	}
	for(int i = 1;i <= cnt;i++) {
		for(int j = 1;j <= time;j++) {
			dp[i & 1][j] = dp[(i - 1) & 1][j]; 
			if(j >= t[i]) dp[i & 1][j] = max(dp[i&1][j],dp[(i - 1)&1][j - t[i]] + c[i]);
		}
	}
	printf("%lld\n",dp[cnt&1][time]);
    return 0;
}
  • 12
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值