題解:CF704B Ant Man

ccxswl發表於2024-10-04

來的,套路都一樣,預設型 DP。

把那個式子拆開,看每個數單獨的貢獻。

  • 一個數比它左邊的數小,它的貢獻就是:\(-x_i + b_i\)
  • 比它左邊的數大,它的貢獻就是:\(x_i + a_i\)
  • 比它右邊的數小,它的貢獻就是:\(-x_i + d_i\)
  • 比它右邊的數大,它的貢獻就是:\(x_i + c_i\)

即:

int Gl(int i) { // > 左邊
    return x[i] + a[i];
}
int Gr(int i) { // > 右邊
    return x[i] + c[i];
}
int Ll(int i) { // < 左邊
    return -x[i] + b[i];
}
int Lr(int i) { // < 右邊
    return -x[i] + d[i];
}

\(f_{i,j}\) 表示已經插入 \(i\) 個數,這些數構成了 \(j\) 個連續段。

新插入的數比後插入的數小,所以一個數插進去的時候可以直接根據前後有沒有數來計算貢獻。

分類討論即可,我分的情況比較細緻,其實有幾個情況有重合,但勝在好理解。

如果 \(i=S\)

  • 插入到最左邊,形成單獨的塊:
    • \(f_{i,j} = f_{i-1,j-1}+Lr(i)\)
  • 插入到最左邊塊的左邊。如果 \(E\) 已經插入,並且 \(j=1\),這種情況就不成立了:
    • \(f_{i,j} = f_{i-1,j}+Gr(i)\)

如果 \(i=E\)

  • 插入到最右邊,形成單獨的塊:
    • \(f_{i,j} = f_{i-1,j-1}+Ll(i)\)
  • 插入到最右邊塊的右邊。如果 \(S\) 已經插入,並且 \(j=1\),這種情況不成立:
    • \(f_{i,j} = f_{i-1,j}+Gl(i)\)

接下來看平常的情況:

  • 插入到所有數最左邊,構成一個新塊。如果 \(S\) 已經插入則不成立:
    • \(f_{i,j} = f_{i-1,j-1}+Ll(i)+Lr(i)\)
  • 插入到最左邊塊的左邊。如果 \(S\) 已經插入則不成立:
    • \(f_{i,j} = f_{i-1,j}+Ll(i)+Gr(i)\)
  • 插入到所有數最右邊,構成一個新塊。如果 \(E\) 已經插入則不成立:
    • \(f_{i,j} = f_{i-1,j-1}+Ll(i)+Lr(i)\)
  • 插入到最右邊塊的右邊。如果 \(E\) 已經插入則不成立:
    • \(f_{i,j} = f_{i-1,j}+Gl(i)+Lr(i)\)
  • 插到中間某塊的左邊。注意一定要有中間塊,所以要滿足 \(j>1\)
    • \(f_{i,j} = f_{i-1,j}+Ll(i)+Gr(i)\)
  • 插到中間某塊的右邊。注意一定要有中間塊,所以要滿足 \(j>1\)
    • \(f_{i,j} = f_{i-1,j}+Gl(i)+Lr(i)\)
  • 插到中間單獨成塊。兩邊一定要有塊,所以要滿足 \(j > 2\)
    • \(f_{i,j} = f_{i-1,j-1}+Ll(i)+Lr(i)\)
  • 插到中間連結兩個塊:
    • \(f_{i,j} = f_{i-1,j+1}+Gl(i)+Gr(i)\)

對於 DP 邊界詳細見程式碼。

程式碼用一些技巧合並了重複的情況。

#include <bits/stdc++.h>

using namespace std;

#define int long long

const int maxN = 5007;

int n, f[maxN][maxN];
int S, T;

int x[maxN], a[maxN], b[maxN], c[maxN], d[maxN];
int Gl(int i) {
    return x[i] + a[i];
}
int Gr(int i) {
    return x[i] + c[i];
}
int Ll(int i) {
    return -x[i] + b[i];
}
int Lr(int i) {
    return -x[i] + d[i];
}

int chkmin(int &x, int y) {
    return x = x > y ? y : x;
}

signed main() {
    cin >> n >> S >> T;
    for (int i = 1; i <= n; i++) cin >> x[i];
    for (int i = 1; i <= n; i++) cin >> a[i];
    for (int i = 1; i <= n; i++) cin >> b[i];
    for (int i = 1; i <= n; i++) cin >> c[i];
    for (int i = 1; i <= n; i++) cin >> d[i];

    memset(f, 0x3f, sizeof(f));
    if (S == 1)
        f[1][1] = Lr(1);
    else if (T == 1)
        f[1][1] = Ll(1);
    else
        f[1][1] = Ll(1) + Lr(1);
    for (int i = 2; i < n; i++) {
        for (int j = 1; j <= i; j++) {
            if (i == S || i == T) {
                if (i == S) {
                    chkmin(f[i][j], f[i - 1][j - 1] + Lr(i));
                    if (j > (i > T))
                        chkmin(f[i][j], f[i - 1][j] + Gr(i));
                }
                else {
                    chkmin(f[i][j], f[i - 1][j - 1] + Ll(i));
                    if (j > (i > S))
                        chkmin(f[i][j], f[i - 1][j] + Gl(i));
                }
                continue;
            }
            if (i > S && i > T && j == 1) continue;

            if (j > (i > T) + (i > S))
                chkmin(f[i][j], f[i - 1][j - 1] + Ll(i) + Lr(i));
            chkmin(f[i][j], f[i - 1][j + 1] + Gl(i) + Gr(i));
            if (i < S || j != 1)
                chkmin(f[i][j], f[i - 1][j] + Ll(i) + Gr(i));
            if (i < T || j != 1)
                chkmin(f[i][j], f[i - 1][j] + Gl(i) + Lr(i));
        }
    }
    if (T == n)
        f[n][1] = f[n - 1][1] + Gl(n);
    else if (S == n)
        f[n][1] = f[n - 1][1] + Gr(n);
    else
        f[n][1] = f[n - 1][2] + Gl(n) + Gr(n);

    cout << f[n][1] << '\n';
}

相關文章