题目描述
Mark 在无意中了解到了 Elf 的身世。在和 James 商量过之后,好心的他们 打算送 Elf 返回故乡。然而,去往 Gliese 的飞船票价高的惊人,他们暂时还付 不起这笔费用。经过一番考虑,Mark 打算去额外做一些工作来获得收入。 经过一番调查,Mark 发现有 N 个工作可以做。做第 i 件工作所需要的时间为 Di,同时也需要一个能力值 Ci 才可以去做,每件工作都可以在任意时间开 始,也可以做任意多次。所有的工作给付的报酬都是一致的。同时,有 S 个课 程可以参加,我们认为今天是第 0 天,第 i 个课程在第 Mi 天开始,持续时间 为 Li 天,课程结束之后能力值会变为 Ai。现在 Mark 的能力值为 1。Mark 只 能做工作到第 T 天(因为那是飞船起飞的日子)。
他想知道期限内他最多可以做多少件工作,好决定未来的打算。于是他找到 了 applepi。でも、applepi は彼女と一緒に楽しむことが大切だ,所以这个任务 就交给你了。
输入输出格式
输入格式:
第一行包含三个空格分隔的整数 T,S,N。 之后 S 行,每行三个整数 M,L,A,描述一个课程。 之后 N 行,每行两个整数 C,D,描述一件工作。
输出格式:
一个整数,表示 Mark 最多可以做多少件工作。
输入输出样例
输入样例 | 输出样例 |
10 1 2 |
6 |
说明
第 0 天至第 2 天做第二件工作 1 次,第 3 天至第 4 天参加课程,能力值变为 5。然后第 5 天至第 9 天做第一件工作 5 次。第 10 天 Mark 不可以继续做工作了。所以 Mark 最多做 6 次工作。
对于 20% 的数据,T,S,N≤10。
对于 50% 的数据,T,N≤1000。
对于 100% 的数据,S≤100,M,L≤10000,A≤100。N≤10000,C≤100, D≤10000,T≤10000。
题解
根据题目,我们尝试用动态规划来解决这道题。
状态:f[i][j]第i天时能力值为j的最大工作量
转移:
对于上面的转移,我们有一下的一些优化:
-
- 对于每一个能力值i,我们都可以找到消耗时间最少且需要能力不超过i的工作,记作po[i]。
- 对于每一个在 i 时刻结束,将能力值改变为j 的课程的开始时间可以记作ke[i][j]。在输入时通过ke[m + l][a] = m获得。
- 对于每一个阶段(天数)我们可以得到一个最大的工作记作量记作g[i]表示第 i 天的工作量最大为g[i]。通过g[i] 我们可以快速地转移上课那种情况的状态。
- 我们可以用maxc表示出现过的最大的能力值(减少状态)。
由此我们的状态转移方程就变成了这样:
初始值:
这是这道题最神奇的地方了,他的初始值分为两部分:
f[0][1] = 0表示的是第0天时能力值为1,且无法工作。
对于其余的f[][]则要赋初值为负无穷。
为什么呢?因为对于这道题中有很多的状态是无效状态,对于无效状态f[i][j]是无法在第 i 天之前得到 j 的能力值却在 i 天的时候有用 j 的能力值去做了工作(即对答案产生了贡献),比如说在输入样例中,f[2][4] , f[2][5]就是无效状态。我们对比分析一下有效状态和无效状态之间的关系,我们发现有效状态只会由有效状态转移得到,那么我们的问题就是找到最开始的有效状态是什么了,根据题目中的“现在 Mark 的能力值为 1”和“我们认为今天是第 0 天”我们可以确定最初的有效状态是f[0][1] = 0。又因为我们的g数组要求当前阶段的所有状态的最大值(包括无效状态),所以我们要消除掉所有无效状态对答案的影响,注意到我们的有效状态只会通过工作这种转移方式转移到无效状态,而这种转移方式对答案的改变每次只+1,而其余的转移都是取min和取max,所以我们就可以像上面那样赋初值。
代码
#include <bits/stdc++.h> using namespace std; int ke[10005][105], g[10005]; int f[10005][105]; int po[10005]; int main() { // freopen("wrk.in", "r", stdin); // freopen("wrk.out", "w", stdout); memset(f, 128, sizeof(f)); memset(g, 128, sizeof(g)); memset(po, 127, sizeof(po)); int t, s, n, m, l, a, c, d, maxc = 0; scanf("%d%d%d", &t, &s, &n); for(int i = 1; i <= s; ++ i) { scanf("%d%d%d", &m, &l, &a); maxc = max(maxc, a); ke[m + l][a] = max(ke[m + l][a], m); } for(int i = 1; i <= n; ++ i) { scanf("%d%d", &c, &d); maxc = max(maxc, c); po[c] = min(po[c], d); } for(int i = 2; i <= maxc; ++ i) po[i] = min(po[i], po[i - 1]); f[0][1] = 0; for(int i = 1; i <= t; ++ i) { for(int j = 1; j <= maxc; ++ j) if(ke[i][j]) f[i][j] = max(f[i][j], g[i - ke[i][j] + 1]); for(int j = 1; j <= maxc; ++ j) { if(i - po[j] >= 0) f[i][j] = max(f[i - po[j]][j] + 1, f[i][j]); f[i][j] = max(f[i][j], f[i - 1][j]); g[i] = max(g[i], f[i][j]); } } printf("%lld ", g[t]); }