[ABC232F] Simple Operations on Sequence 题解
我们把问题陈述中的第一个操作称为 "增/减",第二个操作称为 "交换"。
在这里,让我们对问题陈述稍加限制,一旦执行了增/减操作,就不能再执行交换操作。
换句话说,唯一允许的操作过程是:
- 首先执行任意次数(可能为零)的交换;
- 然后执行任意次数(可能为零)的递增/递减。
这一修改不会改变答案。因为 "递增/递减后交换 "可以替换为 "交换后递增/递减"。
从初始状态 \(A = (A_1, A_2, \ldots, A_N)\) 出发,考虑 "交换任意次数 "步骤完成后的状态,其中 \(A\) 可以表示为 \(A = (A_{P_1}, A_{P_2}, \ldots, A_{P_N})\) 。这里, \(P = (P_1, P_2, \ldots, P_N)\) 是 \(1, 2, 3, \ldots, N\) 的排列。
要使 \(A\) 等于 \(B\) 所需的总成本是多少?
-
首先,在 "任意次数交换 "步骤中,将 \(A = (A_1, A_2, \ldots, A_N)\) 修改为 \(A = (A_{P_1}, A_{P_2}, \ldots, A_{P_N})\) 需要 \(\mathrm{inv}(P)\) 次交换,其中 \(\mathrm{inv}(P)\) 是 \(P\) 的转换数。这需要 \(\mathrm{inv}(P) \cdot Y\) 日元。
-
接下来,在 "递增/递减任意次数 "步骤中,要使 \(A = (A_{P_1}, A_{P_2}, \ldots, A_{P_N})\) 等于 \(B\) ,需要 \(\displaystyle\sum_{i = 1}^N |A_{P_i} - B_i|\) 次递增/递减(其中 \(|\cdot|\) 表示绝对值)。这需要 \(\displaystyle\sum_{i = 1}^N |A_{P_i} - B_i| \cdot X\) 日元。
因此
\(\mathrm{cost}(P) = \displaystyle\sum_{i = 1}^N |A_{P_i} - B_i|\cdot X + \mathrm{inv}(P)\cdot Y\) .
因此,这个问题可以归结为 "通过选择合适的 \(P\) 来最小化 \(\mathrm{cost}(P)\) 的优化问题"。
这里是
\(\mathrm{cost}(P)\)
\(=\displaystyle\sum_{i = 1}^N |A_{P_i} - B_i|\cdot X + \mathrm{inv}(P)\cdot Y\)
\(= \displaystyle\sum_{i = 1}^N |A_{P_i} - B_i| \cdot X + \Big( \displaystyle\sum_{i=1}^N \#\lbrace j : j > i, P_j < P_i \rbrace \Big) \cdot Y\)
\(= \displaystyle\sum_{i = 1}^N (|A_{P_i} - B_i| \cdot X + \#\lbrace j : j > i, P_j < P_i \rbrace \cdot Y)\)
\(= \displaystyle\sum_{i = 1}^N (|A_{P_i} - B_i| \cdot X + \#\lbrace p : p \in \mathcal{N} \setminus \lbrace P_1, P_2, \ldots, P_{i-1}\rbrace, p < P_i \rbrace \cdot Y)\)
其中 \(\#\) 表示元素个数, \(\mathcal{N} := \lbrace 1, 2, \ldots, N\rbrace\) 表示元素个数。
这里要注意, \(\#\lbrace p : p \in \mathcal{N} \setminus \lbrace P_1, P_2, \ldots, P_{i-1}\rbrace, p < P_i \rbrace\) 只取决于 \(\lbrace P_1, P_2, \ldots, P_{i-1} \rbrace\) 和 \(P_i\) ((而不是实际的排列 \(P_1, P_2, \ldots, P_{i-1}\))。
因此,对于 \(S \subseteq \mathcal{N}\) 和 \(x \in \mathcal{N} \setminus S\) 来说,定义
\(f(S, x) := \#\lbrace p : p \in \mathcal{N} \setminus S, p < x \rbrace\),
则
\(\mathrm{cost}(P) = \displaystyle\sum_{i = 1}^N (|A_{P_i} - B_i| \cdot X + f(\lbrace P_1, P_2, \ldots, P_{i-1} \rbrace, P_i) \cdot Y).\)
因此,原来的问题可以改写如下。
你将按照 \(P_1, P_2, \ldots, P_N\) 的顺序确定 \(P\) 的元素。当第一个 \((i-1)\) 元素 \(P_1, P_2, \ldots, P_{i-1}\) 已经确定时,选择 \(x\) 作为下一个元素 \(P_i\) 的代价是
\(|A_{x} - B_i| \cdot X + f(\lbrace P_1, P_2, \ldots, P_{i-1} \rbrace, x) \cdot Y.\)
求确定 \(P\) 中所有元素的最小总成本。
我们将用动态程序设计法(DP)来解决这个问题。
当 \(P\) 的第一个 \(i\) 元素,即 \(P_1, P_2, \ldots, P_i\) 和 \(S = \lbrace P_1, P_2, \ldots, P_i \rbrace\) 已经确定,那么我们就称这个状态为 \(S\) 。
当没有确定 \(P\) 中的元素时,状态为 \(\emptyset\) 。每当 \(P\) 中的一个元素被确定,当前状态就会发生变化,当 \(P\) 中的所有元素都被确定后,当前状态就会达到 \(\mathcal{N}\) 。
对于 \(S \subseteq \mathcal{N}\) ,定义 \(\mathrm{dp}[S]\) 如下。
$\mathrm{dp}[S] := $ (到达状态 \(S\) 所需的最小总成本)
原问题的答案是达到状态 \(\mathcal{N}\) 所需的最小总成本,即 \(\mathrm{dp}[\mathcal{N}]\) :
首先,考虑 DP 的初始化。根据 \(\mathrm{dp}[\ast]\) 的定义、
\(\mathrm{dp}[\emptyset] = 0\) .
另外,为了方便起见,我们将 \(S \neq \emptyset\) 初始化为
\(\mathrm{dp}[S] \leftarrow \infty\) .
接下来,考虑 DP 的转换。
当当前状态为状态 \(S\) 时,我们可以选择 \(\mathcal{N} \setminus S\) 中的任意元素 \(P_{\#S+1}\) 作为下一个元素。选择 \(P_{\#S+1} = x \in \mathcal{N} \setminus S\) 需要花费 \(|A_{x} - B_{\#S+1}| \cdot X + f(S, x) \cdot Y\) 的代价,从而使当前状态变为状态 \(S \cup \lbrace x \rbrace\) 。
相应地,对于每个 \(x \in \mathcal{N} \setminus S\) ,我们有如下的转换:
\(\mathrm{dp}[S \cup \lbrace x\rbrace] \leftarrow \min( \mathrm{dp}[S \cup \lbrace x\rbrace], \mathrm{dp}[S] + |A_{x} - B_{\#S+1}| \cdot X + f(S, x) \cdot Y)\)
通过上述初始化和转换计算出 \(\mathrm{dp}[\ast]\) 后,该问题的答案为 \(\mathrm{dp}[\mathcal{N}]\) 。
因此,解决这个问题总共需要 \(\mathrm{O}(N^2\cdot 2^N)\) 时间(或 \(\mathrm{O}(N\cdot 2^N)\) 时间,取决于实现方式)。
C++ 代码:
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
const ll mod = 1e9 + 7;
const int N = 200005;
const ll INF = 1e18;
ll n, X, Y;
ll a[N], b[N], f[1 << 18];
int check(int s, int x) {
int res = 0;
for (int p = 1; p <= n; p++) {
if ((s & (1 << (p - 1))) == 0 && p < x) res++;
}
return res;
}
int main() {
scanf("%lld%lld%lld", &n, &X, &Y);
for (int i = 1; i <= n; i++) scanf("%lld", &a[i]);
for (int i = 1; i <= n; i++) scanf("%lld", &b[i]);
for (int i = 1; i < (1 << n); i++) {
f[i] = INF;
}
for (int s = 0; s < (1 << n); s++) {
int cnt = 0;
for (int i = 1; i <= n; i++)
if (s & (1 << (i - 1))) cnt++;
for (int x = 1; x <= n; x++) {
if (s & (1 << (x - 1))) continue;
f[s | (1 << (x - 1))] = min(f[s | (1 << (x - 1))], f[s] + abs(a[x] - b[cnt + 1]) * X + check(s, x) * Y);
}
}
printf("%lld\n", f[(1 << n) - 1]);
return 0;
}

浙公网安备 33010602011771号