洛谷 P1833 樱花 (混合背包DP)
摘要:(此摘要和上述代码的注释均为AI生成)该题目描述了一个混合背包问题,涉及三类樱花树赏花方案:只能看一次、最多看Pi次和无限次看。需要计算在限定时间内的最大美学值。解法先将时间差转换为分钟作为背包容量,通过二进制拆分处理多重背包物品,再使用动态规划求解。动态规划采用一维数组记录不同剩余时间下的最大美学值,最终输出结果。题目保证时间差不超过1000分钟,樱花树数量不超过10000棵。
目录
P1833 樱花
题目背景
《爱与愁的故事第四弹·plant》第一章。
题目描述
爱与愁大神后院里种了 n 棵樱花树,每棵都有美学值 Ci(0 < Ci<=200)。爱与愁大神在每天上学前都会来赏花。爱与愁大神可是生物学霸,他懂得如何欣赏樱花:一种樱花树看一遍过,一种樱花树最多看 Pi(0 <= Pi <= 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
输入 #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 次。
思路
问题分析
题目是一个混合背包问题,包含三种物品:只能选一次的樱花树(01背包)、最多选Pi次的樱花树(多重背包)、可以选无限次的樱花树(完全背包)。需要将时间转换为分钟差作为背包容量,通过二进制拆分处理多重背包物品,最后使用动态规划求解最大美学值。
做法
时间转换处理
先把时间转为题目中运算所要求的时间(简单的公式操作)
将输入的时间格式hh:mm
转换为分钟差:
- 计算开始时间
Ts
和结束时间Te
的总分钟数 - 时间差
W = (Te_hh * 60 + Te_mm) - (Ts_hh * 60 + Ts_mm)
在运算操作时在遇到Pi=0时用最大值,使整个DP过程成为一个完全背包问题,最后用背包DP的解法即可,压缩时间可用二分拆分和快速读入函数
动态规划
使用一维数组dp
进行状态转移:
dp[j]
表示剩余时间为j
分钟时的最大美学值- 状态转移方程:
dp[j] = max(dp[j], dp[j - a[i]] + b[i])
,其中a[i]
为时间消耗,b[i]
为美学值
代码
#include <bits/stdc++.h>
using namespace std;
int n, W; // 樱花树数量和时间差(背包容量)
int t, c, p; // 当前樱花树的时间、美学值、次数限制
int a[100005]; // 拆分后的时间消耗数组
int b[100005]; // 拆分后的美学值数组
int cur; // 当前拆分后的物品数量
int dp[1005]; // DP数组,dp[j]表示剩余时间j时的最大美学值
// 快速读入函数(适用于大数据量)
inline int read() {
int x = 0, f = 1;
char ch = getchar();
while (ch < '0' || ch > '9') {
if (ch == '-') f = -1;
ch = getchar();
}
while (ch >= '0' && ch <= '9') {
x = x * 10 + ch - '0';
ch = getchar();
}
return x * f;
}
int main() {
// 处理时间输入(格式为hh:mm)
int a1, a2, b1, b2;
char cc; // 用于读取冒号
cin >> a1 >> cc >> a2 >> b1 >> cc >> b2;
// 计算时间差(转换为分钟)
W = 60 * (b1 - a1) + (b2 - a2);
// 读取樱花树数量
n = read();
// 处理每棵樱花树
while (n--) {
t = read(); // 时间消耗
c = read(); // 美学值
p = read(); // 次数限制
// 完全背包处理(Pi=0表示无限次)
if (p == 0) {
p = W / t; // 最多可选次数
}
// 多重背包二进制拆分
int base = 1;
while (p >= base) {
cur++;
b[cur] = base * c; // 拆分后的美学值
a[cur] = base * t; // 拆分后的时间消耗
p -= base;
base <<= 1; // 二进制增长
}
// 处理剩余次数
if (p > 0) {
cur++;
a[cur] = p * t;
b[cur] = p * c;
}
}
// 01背包动态规划
for (int i = 1; i <= cur; i++) {
for (int j = W; j >= a[i]; j--) {
dp[j] = max(dp[j], dp[j - a[i]] + b[i]);
}
}
// 输出结果
cout << dp[W];
return 0;
}
这就是整篇题的题解
谢谢大家
更多推荐
所有评论(0)