# 练习
## CSES 1195
本题有几种做法,这里全部介绍。
### 方法 1:正反图 + 枚举边
#### 直觉
1. 任意一条最优解路径一定经过一条使用优惠券的边 $(u, v, w)$(表示一条从 $u$ 到 $v$ 的边权为 $w$ 的边)。可以枚举使用优惠券的边,求出当前情况下的最优解,再对所有边对应的最优解求一个最小值。
2. 把最优解路径拆分为三段:
$
1 \leadsto u \text{的最短路} + \left \lfloor \dfrac{w}{2} \right \rfloor + v \leadsto n \text{的最短路}
$
3. 通过预处理最短路,降低时间复杂度
#### 做法
1. 在原图上从起点 $1$ 跑一次 Dijkstra,得到
$
dist1[u] = \text{从 1 到 u 的最短距离}
$
2. 在反图上从起点 $n$ 跑一次 Dijkstra,得到
$
distN[v] = \text{从 v 到 n 的最短距离}
$
3. 只能使用一张优惠券,枚举边 $(u, v, w)$ 作为使用优惠券的边,求出当前情况下的最优解,再对所有边对应的最优解求一个最小值,即答案为
$
\min_{(u\to v,w)\in E} dist1[u] + \left \lfloor \frac{w}{2} \right \rfloor + distN[v]
$
#### 时间复杂度
- 两次 Dijkstra(优先队列实现),$O(m \log m)$
- 枚举所有边求解最优解,$O(m)$
总时间 $O(m \log m)$。
#### 空间复杂度
- 原图和反图的邻接表,$O(n + m)$
- 记录最短路的数组,$O(n)$
- Dijkstra 的优先队列实现中优先队列所占空间,$O(m)$
总空间 $O(n + m)$。
#### 方法 2:状态图(分层图)
可以建立两层状态图。状态(点)和转移(边)描述如下:
- 状态 $(u, 0 / 1)$ 表示当前在点 $u$、是否用过优惠券
- 从 $(u, 0)$ 走到 $(v, 0)$,边权为 $w$,表示不使用优惠券
- 从 $(u, 0)$ 走到 $(v, 0)$,边权为 $\left \lfloor \dfrac{w}{2} \right \rfloor$,表示在原图上的 $u \to v$ 这条边使用优惠券
- 从 $(u, 1)$ 走到 $(v, 1)$,边权为 $w$,表示已经使用完优惠券后正常走
对这 $2n$ 个点跑一遍从 $(1, 0)$ 开始的最短路,答案是 $(n, 1)$ 的最短路。
在实现上有两种方法。
隐式建图:设置数组 $dist[u][0/1]$,然后直接在原图上用 Dijkstra 进行状态转移。对于 Dijkstra 中枚举当前点的出边 $(u \to v, w)$:
- 不使用优惠券和已经使用过优惠券,$dist[u][0] + w \to dist[v][0], dist[u][1] + w \to dist[v][1]$
- 在这条边使用优惠券,$dist[u][0] + \left \lfloor \frac{w}{2} \right \rfloor \to dist[v][1]$
显式建图:建立 $2n$ 个点,对于原图上的每一条边 $(u \to v, w)$,在新图上建立边 $(u \to v, w), (u \to v + n, \left \lfloor \frac{w}{2} \right \rfloor), (u + n \to v + n, w)$。目标改为求新图上点 $1$ 到 $2n$ 的最短路
两种实现方法的时空复杂度一致。
#### 时间复杂度
一共有 $2n$ 个状态(点)、$3m$ 次转移(边),跑一次 Dijkstra,时间 $O(3m \log (3m)) = O(m \log m)$。
#### 空间复杂度
- 显示建图中新图的邻接表,$O(2n + 3m) = O(n + m)$;隐式建图中原图的邻接表,$O(n + m)$
- 记录最短路的数组,$O(2n) = O(n)$
- Dijkstra 的优先队列实现中优先队列所占空间,$O(3m) = O(m)$
总空间 $O(n + m)$。
## 洛谷 P4568
状态图(分层图)。
我们可以使用显示建图:
- 不使用优惠券,建无向边 $(a + pn \leftrightarrow b + pn, c)$,其中 $0 \le p \le k$
- 使用优惠券,建有向边 $(a + pn \to b + (p + 1)n, c)$,其中 $0 \le p \lt k$
也可以不建图,直接在原图用 Dijkstra 做状态转移。对于边 $(u \to v, w)$:
- 不使用优惠券,$dist[u][p] + w \to dist[v][p]$,其中 $0 \le p \le k$
- 使用优惠券,$dist[u][p] \to dist[u][p + 1]$,其中 $0 \le p \lt k$
在本题中,隐式建图更加方便。以下时空复杂度分析采用隐式建图方法。
#### 时间复杂度
一共有 $(k + 1)n$ 个状态,每个状态最多两次转移(是否使用优惠券),跑一次 Dijkstra,时间 $O(kn \log (kn))$。
#### 空间复杂度
- 原图的邻接表,$O(n + m)$
- 记录最短路的数组,$O(kn) = O(n)$
- Dijkstra 的优先队列实现中优先队列所占空间,$O(3m) = O(m)$
总空间 $O(n + m)$。