【2024 ZR-C Day 11】DP 优化(2)

1. 四边形不等式优化 DP

1.1. 决策单调性

决策单调性:记转移中每个点 \(i\) 的决策点为 \(opt(i)\),对于任意 \(i_1 < i_2\),必然成立 \(\mathop{\mathrm{opt}}(i_1) \leq \mathop{\mathrm{opt}}(i_2)\),则称具有“决策单调性”。

1.2. 四边形不等式

四边形不等式:如果对于任意 \(a\leq b\leq c\leq d\) 均成立 \(w(a,c)+w(b,d) \leq w(a,d)+w(b,c)\),则称函数 \(w\) 满足四边形不等式(简记为「交叉小于包含」)。

通常而言,只需取所有 \(a = i, b = i + 1, c = j, d = d + 1\) 进行验证。

对于一个这样的问题:\(\displaystyle f(i) = \min_{1 \leq j < i} (f_j + w(j,i)) \qquad \left(1 \leq i \leq n\right)\),若 \(w\) 满足四边形不等式,则该问题满足决策单调性

(若是形如 \(\displaystyle f(i) = \max_{1 \leq j < i} (f_j + w(j,i))\) 的问题,考虑将左右两边取相反数,即 \(\displaystyle -f(i) = \min_{1 \leq j < i} (-f_j - w(j,i))\) 的形式,从而可证 \(-w\) 的四边形不等式。)

而在满足决策单调性时,有两种常见算法可以将算法复杂度优化到 \(O(n\log n)\).

1.3. 分治优化

适用于所有离线 DP 的问题。

要求解所有状态,只需要求解所有最优决策点。
为了对所有 \(1 \leq i \leq n\) 求解 \(\mathop{\mathrm{opt}}(i)\),首先计算 \(\mathop{\mathrm{opt}}(\left\lfloor \frac{n}{2} \right\rfloor)\),而后分别计算 \(1 \leq i < \left\lfloor \frac{n}{2} \right\rfloor\)\(\left\lfloor \frac{n}{2} \right\rfloor < i \leq n\) 上的 \(\mathop{\mathrm{opt}}(i)\),注意此时已知前半段的 \(\mathop{\mathrm{opt}}(i)\) 必然位于 \(1\)\(\mathop{\mathrm{opt}}(\left\lfloor \frac{n}{2} \right\rfloor)\) 之间(含端点),而后半段的 \(\mathop{\mathrm{opt}}(i)\) 必然位于 \(\mathop{\mathrm{opt}}(\left\lfloor \frac{n}{2} \right\rfloor)\)\(\mathop{\mathrm{opt}}(n)\) 之间(含端点)。
对于两个子区间,也类似处理,直至计算出每个问题的最优决策。

在分治的过程中记录搜索的上下边界,就可以保证算法复杂度控制在 \(O(n\log n)\).

void solve(int l, int r, int k_l, int k_r)
{
	int mid = (l + r) / 2, k = k_l;
	// 求状态f[mid]的最优决策点
	for(int j = k_l; j <= min(k_r, mid - 1); ++j)
		if(w(j, mid) < w(k, mid)) k = j;
	f[mid] = w(k, mid);
	// 根据决策单调性得出左右两部分的决策区间,递归处理
	if(l < mid) solve(l, mid - 1, k_l, k);
	if(r > mid) solve(mid + 1, r, k, k_r);
	return;
}

1.4. 二分队列优化

注意到对于每个决策点 \(j\),能使其成为最小最优决策点的问题 \(i\) 必然构成一个区间。
可以通过单调队列记录到目前为止每个决策点可以解决的问题的区间,这样,问题的最优解自然可以通过队列中记录的决策点计算得到。算法大致如下。

  • 队列需要记录到目前为止每个可行的决策点 \(j\) 和能够解决的问题区间左右端点 \(l_j\)\(r_j\) 构成的三元组。对于给定区间 \([l_j,r_j]\) 内的问题,\(j\) 应该是到目前为止考虑过的决策点中最小最优的(以下简称最优决策)。每时每刻,队列中存储的决策未必是连续的,但是尚未解决的问题应该是队列中存储的问题区间的不交并。
  • 初始化:将首个决策放于队列中,并记录它对于所有问题都是最优的。
  • 类似于单调队列,每次考虑下一个决策 j 的时候,都需要进行出队和入队操作。
  • 出队:当所有决策 \(j \leq i\) 都考虑结束后,问题 \(i\) 的解就是队列中首个满足 \(l_j \leq i \leq r_j\) 的决策点 \(j\). 此时可以弹出所有满足 \(r_j < i\) 的队首。由于决策单调性,弹出的决策也不会是后续问题的最优决策。
  • 入队:要对决策 \(j\) 进行入队时,首先比较它和队尾的决策 \(j'\).
    • 如果对于问题 \(l_{j'}\),将入队的决策 \(j\) 比已有的决策 \(j'\) 更优,即 \(w(j,l_{j'}) < w(j',l_{j'})\) 时,则弹出队尾的决策 \(j'\). 此操作持续到队尾的决策 \(j'\) 比起 \(j\) 对于问题 \(l_{j'}\) 更优时为止。
    • 如果队列已空,入队 \((j,j+1,n)\),即认为决策 \(j\) 是尚未解决的所有问题的最优解。
    • 如果队尾决策 \(j'\) 对于问题 \(r_{j'}\) 同样优于将入队的决策 \(j\),那么当 \(r_{j'} < n\) 时,入队 \((j,r_{j'}+1,n)\),表示 \(j\) 是对于问题 \([r_{j'}+1,n]\) 的最优解,否则,不需要入队 \(j\),因为它并不比已有的决策更优。
    • 最后的情形是,队尾决策 \(j'\) 比起要入队的决策 \(j\) 对于问题 \(l_{j'}\) 更优,而对于问题 \(r_{j'}\) 更劣,那么,需要通过二分找到最小的 \(i\in[l_{j'},r_{j'}]\) 使得 \(w(j,i) < w(j',i)\),将队尾的区间右端点修改为 \(i-1\),并入队 \((j,i,n)\).
int lt[N], rt[N], f[N];
deque<int> q;
// 初始化队列
void solve()
{
	q.emplace_back(1);
	lt[1] = 1, rt[n] = n;
	// 顺次考虑所有问题和决策
	for (int j = 1; j <= n; ++j)
	{
		// 出队
		while(!q.empty() && rt[q.front()] < j) q.pop_front();
		// 计算
		f[j] = w(q.front(), j);
		// 入队
		while(!q.empty() && w(j, lt[q.back()]) < w(q.back(), lt[q.back()])) q.pop_back();
		if(q.empty())
		{
			q.push_back(j);
			lt[j] = j + 1, rt[j] = n;
		}
		else if(w(j, rt[q.back()]) < w(q.back(), rt[q.back()]))
		{
			if(rt[q.back()] < n)
			{
			  q.push_back(j);
			  lt[j] = rt[q.back()] + 1;
			  rt[j] = n;
			}
		}
		else
		{
			int L = lt[q.back()], R = rt[q.back()], p;
			// 二分
			while (L <= R)
			{
				int mid = (L + R) >> 1;
				if(w(j, mid) < w(q.back(), mid)) p = mid, rr = mid - 1;
				else ll = mid + 1;
			}
			rt[q.back()] = p - 1;
			q.push_back(j);
			lt[j] = p, rt[j] = n;
		}
	}
	return;
}

1.5. 区间分拆问题

1.5.1. 一般情形

考虑将某个区间拆分成若干个子区间的问题。
形式化地说,将给定区间 \([1,n]\) 拆分成 \([a_1,b_1],\cdots,[a_k,b_k]\),其中,\(a_1=1,b_k=n\),以及 \(b_{i}+1=a_{i+1}\) 对任意 \(i < k\) 都成立。

对于给定拆分,成本为 \(\sum_{i=1}^k w(a_i,b_i)\)。问题要求最小化这一成本。可以列出如下的 1D1D 状态转移方程。

\[f(i) = \min_{1\leq j\leq i} f(j-1)+w(j,i) \qquad (1\leq i\leq n) \]

这里,\(f(0)=0\). 注意到,只要 \(w(j,i)\) 满足四边形不等式,\(f(j-1)+w(j,i)\) 必然满足四边形不等式,因为第一项并不包括 \(j\)\(i\) 的交叉项,在混合差分时会消去。
但是由于成本函数依赖于前面的子问题,这一转移只能够顺序计算,所以通常只适合应用二分队列算法。算法复杂度为 \(O(n\log n)\).

1.5.2. 限制区间个数的情形

上述问题可以加强为限制区间个数的情形,即问题指定将区间拆分成 \(m\) 个子区间。此时需要将拆分后的区间个数作为转移状态的一维。相应地,有 2D1D 状态转移方程如下。

\[f(k,i) = \min_{1\leq j\leq i} f(k-1,j-1)+w(j,i) \qquad (1\leq k\leq m,\ 1\leq i\leq n) \]

这里,\(f(0,0)=0,f(0,i)=f(k,0)=+\infty\) 对任意 \(1\leq k\leq m\)\(1\leq i\leq n\) 都成立。

和上文同样的道理,这里的 \(f(k-1,j-1)+w(j,i)\) 必然满足四边形不等式。
将每一个第一维为 \(k\) 的状态视为第 \(k\) 层,此时对于第 \(k\) 层的计算,只依赖于前面层的计算,与本层无关。所以对于每一层,都可以通过分治或者二分队列的方法进行计算,此时算法复杂度为 \(O(mn\log n)\).

但还可以得到更好的优化。我们有以下的定理:

\(w\) 满足四边形不等式,则对于 2D1D 问题成立 \(\mathop{\mathrm{opt}}(k-1,i) \leq \mathop{\mathrm{opt}}(k,i) \leq \mathop{\mathrm{opt}}(k,i+1)\).

利用这一结果,我们可以限制决策 \(j\) 的搜索范围。算法实现时,对 \(k\) 正向遍历,对 \(i\) 逆向遍历,在之前已确定的上下界范围内暴力搜索 \(j\) 就可以保证 \(O(n(n+m))\) 的算法复杂度。

1.5.3 WQS 二分 / 凸优化

在限定区间数的条件下,还可以使用 WQS 二分 优化 DP.

根据四边形不等式的性质,若 2D1D 转移方程中 \(w(i, j)\) 满足四边形不等式,则所有 \((i, f_i)\) 形成一个凸包。我们称这样的 \(f\) 具有凸性

我们考虑用斜率为 \(p\) 的直线来截 \(f\) 这个凸包,也就是计算 \(f(x) + px\) 的最小值。假设截到的点为 \((m, f(m))\),那么我们就可以算出 \(f(m)\) 的值。

注意到随着 \(p\) 的增大,截到的切点只会单调地进行移动。因此我们二分斜率 \(p\),直到截取到的切点恰好为 \(k\),这样就算出了 \(f(k)\) 的值(对于三点共线的情况,钦定选取个数最小的一个,也就是最靠左的切点)。

考虑给定斜率 \(p\),如何计算 \(f(x) + px\) 的最小值。
它的组合意义是每选择一个物品,就需要额外付出 \(p\) 的代价。设 \(w'(i, j) = w(i, j) + p\),则转移方程变为 \(\displaystyle f_i = \min_{j < i} \{f_j + w'(j, i)\}\).

于是我们就可以以 \(\log V\) 的代价把限制物品个数的情形转化成不限制的情形。
这样的情形即为区间拆分问题 1D1D 方程的形式,可以在 \(O(n \log n)\) 的复杂度内求。

2. 矩阵乘法优化 DP

2.1. 矩阵乘法

\(A\)\(P \times M\) 的矩阵,\(B\)\(M \times Q\) 的矩阵,设矩阵 \(C = A \times B\),其中矩阵 \(C\) 的所有元素可以表示为:\(\displaystyle C_{i, j} = \sum_{k = 1}^M A_{i, k} \cdot B_{k, j}\).

矩阵乘法没有交换律,但满足结合律

根据结合律,我们可以对 \(n \times n\) 的矩阵 \(A\) 做快速幂,快速求 \(A^k\). 复杂度 \(O(n^3) \log k\).

2.2. 广义矩阵乘法

定义 \(A \times B = C\),则 \(\displaystyle C_{i, j} = \max_{k = 1}^M (A_{i, k} + B_{k, j})\).

与一般的矩阵乘法同样地,广义情况也满足结合律,故可以使用快速幂优化。

更进一步的扩展:对于两个符号 \(\operatorname{op_1}, \operatorname{op_2}\),若 \(\operatorname{op_2}\)\(\operatorname{op_1}\) 满足分配律,则都可以得到以 \(\displaystyle C_{i, j} ={\operatorname{op_1}}_{k = 1}^M (A_{i, k} \operatorname{op_2} B_{k, j})\) 定义的扩展矩阵乘法 \(A \times B = C\).

posted @ 2024-07-29 08:26  心灵震荡  阅读(19)  评论(0)    收藏  举报