赞
踩
改编自洛谷题库 P1048采药 https://www.luogu.com.cn/problem/P1048 |
小猪是一名天赋异禀的运动员,从小被父母送进体校进行双人运动培训(例如羽毛球、乒乓球等),小猪在漫长的9年训练过程中逐渐丧失了对双人运动的乐趣。 |
渐渐地,小猪发现自己喜欢的其实是多人运动(例如篮球、足球、王者荣耀等)。但由于害怕教练的责罚,小猪只能偷偷在凌晨的短暂时间内,安排自己的多人运动。 |
而且由于教练行踪不定,每晚能进行多人运动的时间都是不确定的。小猪进行不同的多人运动可以获得不同的快乐点,例如打篮球、足球、王者荣耀,甚至不同段位的多人运动都会产生不同的快乐点h,并且不同的运动所需要的时间t也不同。 |
现在小猪一个晚上有多种可选择的多人运动,聪明的你能帮助小猪在晚上有限的时间点内,完成最佳时间管理吗? |
在有限的时间T内,给定N个多人运动,每个多人运动有以下特点:
在T时间内安排合理的运动,使得小猪能获得总和最多的快乐点
一个整数H,表示小猪可以获得的快乐最大值
240 5
20 399
221 4500
220 4120
45 5
300 558
首先 时间是有限的,所以显然我们只能在诸多可能的运动中选择 如果小猪很贪心,直接进行快乐点最多的运动,那么小猪有没有可能直接获得最多的快乐呢? |
答案是有可能的,但是不能代表全部的情况。 例如上面的例子,显然直接去打篮球是最多快乐的,但是实际上这个决策只能带来4500的快乐,而比不上打一局青铜王者和母猪焊接一起的4510点快乐来的多,因为浪费了19分钟无法安排任何活动。
那么 |
我们对问题进行分析,我们最终的状态是什么?
最终的状态:
时间已经达到开始所给的T分钟
那么为什么效率最高的解法——价值/时间最高的方法不是最好的呢?
因为比如上面的例子,有时间被浪费了
我们需要换个角度来思考了
最终的这个状态是怎么来的?
很显然,最后的240分钟是一分钟一分钟地从0分钟来的,
正如伟大的哲学家所说,劳力士和电子手表,时间是一样转的
不论采取那种组合时间的方式,时间一定会流向终点
比如上述例子,达到最终时间的前一步,一定是做完某一个运动之前的时间,或者一分钟前(无法做运动)
如图,可见要达到最终状态,上一个状态可以是前一分钟(这样什么运动都做不了) |
也可以从19分钟开始做(这样可以穿插安排一个打篮球或者母猪焊接) |
也可以从18分钟开始做(还是可以穿插一个打篮球或者母猪焊接) |
也可以从4分钟开始做(可以打篮球或者母猪焊接) |
也可以从20分钟开始做(可以是母猪焊接) |
因为已经是最后一步了,所以我们无脑选择这一步最优化的策略,也就是原有的快乐值+现在的快乐值之后总和最多 |
因为,如果我们能保证前一步的所有决策都是最优化的决策(也就是前一步的每一分钟都是最优化的决策),那么我们可以确信,我们现在的决策就是最优的。
时间为0,快乐值为0,这一状态有着绝对正确的性质
核心就是 我是谁,我从哪里来,我到哪里去,把大问题无限分割
只要保证每一个小问题绝对正确,那么大问题绝对正确
小学课文《走一步,再走一步》深刻挖掘了这种思想
这样子可能会遇到重复的问题,也就是说,一个“很好的”运动可能会被用多次
因为如果决策中的运动一样,那么进行这些运动的顺序其实是没有关系的,所以我们可以利用这个特性,把最终的状态定义为
当了解到第N个运动的时候,最佳的决策
也就是说,我们从第一个可能的运动开始遍历,第1个第2个、一直到第N个运动,保证了解到第K个可能的运动的时候我们的决策始终是最佳的
我们来结合具体例子看一看 首先小猪来了解王者青铜局 |
由于当前时间充裕,所以此时dp[1][20]之后的比如 dp[1][21] dp[1][22] dp[1][23] 一直到 dp[1][240] 都是20 |
我们取dp[1][240]出来,解释为此时最佳决策 |
然后我们再来看下一个运动打篮球 |
也可以从20分钟开始做(可以是母猪焊接) |
**我们来对比一下
max(dp[1][221] (399), dp[1][19]+va[2] (0+4500))
显然dp[2][221]应该被更新为后者**
也就是说,对于小猪看到打篮球这个第二个运动的时候,小猪发现,原来比起打王者荣耀,还是打篮球更赚喔(小猪是不会考虑以后的,他只考虑当下) |
所以当小猪看到可以打母猪焊接的时候,小猪发现
比起dp[2][240],更加应该被更新为 dp[2][20](已经打了王者)+vi[3](母猪焊接)
之后采取任何决策都不会比这个决策更好了
#include<bits/stdc++.h> #define ios std::ios::sync_with_stdio(false) using namespace std; int va[105];//用来存储运动相应的价值 int ti[105];//用来存存储运动相应的时间 int dp[1050][1050];//用来存储进行到第t1时间时,前一状态的t2时间最大价值 signed main() { ios; int T, N; cin >> T >> N;//输入时间t和可选择的项目n for (int i = 1; i <= N; i++) { cin >> ti[i] >> va[i]; } for (int k = 1; k <= N; k++) { for (int t = 0; t <= T; t++) { dp[k][t] = dp[k - 1][t];//先假设上一个决策即使到了现在还是对的 if (t - ti[k] >= 0) dp[k][t] = max(dp[k][t], dp[k - 1][t - ti[k]] + va[k]);//如果加上现在决策更好那就换 } } for (int i = 1; i <= N; i++) { for (int j = 1; j <= T; j++) { cout << dp[i][j] << " "; } cout << endl; } cout << dp[N][T] << endl; return 0; }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。