题目描述
为了能让更多的同学学习到 IT 技术,蓝桥云课又开始了课程限时打折活动!
作为初学者的你,希望尽可能买到含金量总额更高的课程,当然其他同学也是这么想。
由于购买课程的同学实在太多,蓝桥云课服务器带宽供不应求,导致同学购买课程需要一定的等待时间。
比如现有三门课程:
- 《Java 程序设计》需要等待 3 分钟,打折活动在 3 分钟后结束,该课程含金量为 6。
- 《Python 程序设计》需要等待 2 分钟,打折活动在 2 分钟后结束,该课程含金量为 3。
- 《C#程序设计》需要等待 1 分钟,打折活动在 3 分钟后结束,该课程含金量为 5。
方案一:你可以选择购买《Java 程序设计》,那么你可以抢购到含金量为 6 的课程。
方案二:你可以先购买《Python 程序设计》,等待结束后再购买《C#程序设计》,这样就可以抢购到含金量为 8 的课程。
请注意,只能同时参与一门课程的抢购活动,且开始等待后不允许中途退出,只有在打折活动有效期内才可以抢购课程。
输入格式
第一行是一个整数 N
,代表蓝桥云课中有 N
门课程。
紧接着 N
行,每行三个正整数,A
(购课等待时间),B
(打折活动截止时间),C
(课程含金量)。
输出格式
输出一行一个整数,代表你能购买到课程最大的含金量总值。
样例输入
3
3 3 6
2 2 3
1 3 5
样例输出
8
评测数据规模
对于 20% 的评测数据,
0 < N < 10
1 < A < 20
1 < B < 20
1 < C < 20
,且A ≤ B
。
对于所有评测数据,
0 < N ≤ 50
1 < A < 10^5
1 < B < 10^5
1 < C < 2^31
,且A ≤ B
。
题解
这个问题本质上是一个 时间背包问题(Time-Constrained Knapsack),类似于 0-1 背包问题,但物品(课程)有时间约束,不能随时选择。
这个问题可以通过动态规划来解决。我们需要找到一种策略,在有限的时间内最大化所购得课程的含金量。具体步骤如下:
-
初始化:
- 定义一个动态规划数组
dp[]
,其中dp[j]
表示在第j
分钟所能获得的最大含金量。 - 初始化时,假设所有元素都为0,表示没有进行任何购买操作。
- 定义一个动态规划数组
-
排序与处理:
- 将所有课程按其打折活动截止时间
B
进行升序排序,如果B
相同则按等待时间A
升序排序。
- 将所有课程按其打折活动截止时间
-
状态转移:
- 对于每一个课程,从其截止时间
B
开始向前遍历至其等待时间A
,更新dp[j]
的值,即考虑是否选择当前课程以增加总含金量。
- 对于每一个课程,从其截止时间
-
结果计算:
- 最终,遍历整个
dp[]
数组,找到最大值即为能获得的最大含金量。
注意
排序的原因是 b(截止时间)不同,避免状态污染。
维护 mc = max(mc, dp[j]) 的原因是 a(等待时间)存在,最优值可能在 b_max 之前的某个 j 处。 - 最终,遍历整个
示例代码
#include<bits/stdc++.h>
using namespace std;
using ll = long long;
const int N = 1e5 + 9;
ll dp[N]; // 在j分钟后所能购得的最大含金量
struct node{
int a, b, c;
bool operator < (const node &u)const{
return b == u.b ? a < u.a : b < u.b;
}
}x[N];
int main(){
ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
int n; cin >> n;
for(int i = 1;i <= n;i ++) cin >> x[i].a >> x[i].b >> x[i].c;
ll mc = -1;
sort(x + 1, x + n + 1);
for(int i = 1;i <= n;i ++){
for(int j = x[i].b;j >= x[i].a;j --){
dp[j] = max(dp[j], dp[j - x[i].a] + x[i].c);
mc = max(mc, dp[j]);
}
}
cout << mc << '\n';
return 0;
}