[ABC232F] Simple Operations on Sequence 題解

jxy2012發表於2024-04-13

我們把問題陳述中的第一個操作稱為 "增/減",第二個操作稱為 "交換"。

在這裡,讓我們對問題陳述稍加限制,一旦執行了增/減操作,就不能再執行交換操作。

換句話說,唯一允許的操作過程是:

  • 首先執行任意次數(可能為零)的交換;
  • 然後執行任意次數(可能為零)的遞增/遞減。

這一修改不會改變答案。因為 "遞增/遞減後交換 "可以替換為 "交換後遞增/遞減"。

從初始狀態 \(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;
}

相關文章