算法
考虑 \(\rm{dp}\)
其实谁都知道是 \(\rm{dp}\) , 但是推不出来啊
这个问题的关键点在于注意到每次往回走, 必定需要走到之前只访问过一次的位置, 这样算法才有正确性
容易的, 令 \(f_i\) 表示游览结束前 \(i\) 个点的最小时间花费, 由上面的结论可知, 对于 \(f_i\) 往回走的更新, 一定可以用 \(f_j, j < i\) 来表示, 因为是最小的花费, 显然 \(f_i\) 时刻在 \(i\) 点的位置
考虑转移
注意到仅仅有这样的情况:
先往前走到 \(i\) ( 再往后走是无意义的, 一定不优 ) , 然后在回头走, 最后再绕回 \(i\)
这样有一个问题在于有可能绕回来的过程中依然没有到达时间限制 \(T\) , 于是必须要等到 \(T\) , 此时路过的点一定能够全部访问
在这里我有一个误区, 就是同时考虑了往前走在走回来这一情况, 事实上考虑一种即可覆盖
有转移柿子
\[f_i = \min_{j = 1}^{i - 1} \left\{ f_j + a_i - a_j + \max \left[ 2 \times (a_i - a_{j + 1}), T) \right] \right\} \]显然的, 有 \(f_1 = \max(a_1, T)\) , 答案即为 \(f_n\)
但是这样做是 \(\mathcal{O} (n ^ 2)\) 的, 我们考虑优化
数据结构优化
不太显然的, 将转移柿子中的 \(a_i - a_j\) 提出
有,
\[f_i = a_n + \min_{j = 1}^{i - 1} \left\{ f_j + \max \left[ 2 \times (a_i - a_{j + 1}), T) \right] \right\} \]直接丢进堆里维护即可, 复杂度 \(\mathcal{O} (n \log n)\)
利用单调性优化
需要一些注意力, 我们观察到 \(i\) 一定时, \(2 \times (a_i - a_{j + 1})\) 和 \(f_j\) 是单调递减的, 同时, 注意到当 \(i\) 增加时, 在之前 \(2 \times (a_i - a_{j + 1}) > T\) , 对于更大的 \(i ^ {\prime}\) 显然也有 \(2 \times (a_{i ^ {\prime}} - a_{j + 1}) > T\)
那么我们就可以想到一种做法, 对于每一次更新 \(a_i\) , 我们就在单调队列中把所有满足 \(2 \times (a_i - a_{j + 1}) > T\) 的 \(j + 1\) 弹出, 其中 单调队列按照 \(a_i\) 从小到大排序
弹出点的时候, 记录其最大值 \(k\) , 然后对于队列中的点, 有
$ f_i = a_n + f_j + T $ , 对于弹出的点, 有 \(f_i = a_n + f_j + 2 \times (a_i - k)\)
每个点进队出队一次, 时间复杂度 \(\mathcal{O} (n)\)
事实上因为 \(a_i\) 单调性, 完全可以使用队列处理
代码
略了, 好累啊
总结
有啥好说的, 练 \(\rm{dp}\)
注意考虑转移时不需要考虑本质相同的转移
写博客就该这样, 不能看着题解抄, 自己理一遍
标签:right,队列,11.22,T3,times,考虑,CW,单调,left From: https://www.cnblogs.com/YzaCsp/p/18563637