我們把問題陳述中的第一個操作稱為 "增/減",第二個操作稱為 "交換"。
在這裡,讓我們對問題陳述稍加限制,一旦執行了增/減操作,就不能再執行交換操作。
換句話說,唯一允許的操作過程是:
- 首先執行任意次數(可能為零)的交換;
- 然後執行任意次數(可能為零)的遞增/遞減。
這一修改不會改變答案。因為 "遞增/遞減後交換 "可以替換為 "交換後遞增/遞減"。
從初始狀態 \(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;
}