P3084 [USACO13OPEN]Photo G - 洛谷 计算机科学教育新生态
题目大意
对于一个 01 序列 \(a_1, \cdots a_n\) , 给出 \(m\) 个区间,保证每个区间里有且仅有一个 1 。求序列里最多有多少个 1 。
其中 \(1 \leqslant n \leqslant 200000\) , \(1 \leqslant m \leqslant 100000\) 。
算法选择
可以选择动态规划,也可以选择差分约束。在这里选择最短路差分约束作为练习。
具体思路
设 \(S_n\) 为序列前 \(n\) 项的和,则对于此 01 序列本身 \(S_{i-1} \leqslant S_i \leqslant S_{i-1}+1\) 的性质可以列出方程
\[ \left\{ \begin{aligned} &S_{i-1} - S_i \leqslant 0 \\ &S_i - S_{i-1} \leqslant 1 \quad (i = 1, 2, \cdots , n) \end{aligned} \right. \]
同时由给出的 \(m\) 个条件列出方程
\[ \left\{ \begin{aligned} &S_{b_i} - S_{a_i-1} \leqslant 1 \\ &S_{a_i-1} - S_{b_i} \leqslant -1 \quad (i = 1, 2, \cdots , m) \end{aligned} \right. \]
之后连图跑 SPFA 判负环,计算即可, \(S_n\) 即为所求。
代码实现
- 裸 SPFA 算法只能打出 70pts 。
- SPFA 算法使用 swap-SLF 优化:每次队列改变后检查队首队尾,若是存在
dis[que.front()] > dis[que.end()]
便交换队列首尾。优化后可以拿到 90pts。 - 加入时间检测判负环,即若是时间到达 0.95s 仍进行 SPFA
循环,便直接判断负环存在并跳出。优化后可以 AC 。(
clock()
返回程序运行时间,以clock
为单位。在编译器中,1s 时间对应的 clock 数由宏CLOCKS_PER_SEC
定义,不同系统中不同。) - 实际上,时间检测判负环的结果有一定的错误率,因为实际上算法并没有跑完便中断了。只是我们认为在 1s 的时间内跑不完 SPFA 的数据大概率是存在负环的。
时间复杂度 \(O(KN)\) , 用时 1.40s 。
1 |
|
2020-08-08 Update: 更正了关于 clock()
函数的解释。