1. 程式人生 > 其它 >洛谷 P2885 題解

洛谷 P2885 題解

菜 MilkyCoffee 菜

教練(校外)給我講了這道題後覺得很有必要記錄一下...

明顯有個很明顯的暴力(廢話)

\(f_{i,j} = min(f_{i-1, j'} + c * |j - j'|) + (j - a_i) ^ 2\)

吸氧後能過(@我吃死酸辣粉 測試)

現在我們要進一步優化它

很明顯這個絕對值可以拆成兩個方程的 \(min\)

\(f_{i,j} = min(f_{i-1, j'} + c * (j - j')) + (j - a_i) ^ 2\)

\(f_{i,j} = min(f_{i-1, j'} + c * j - c * j') + (j - a_i) ^ 2\)

\(f_{i,j} = min(f_{i-1, j'} + c * (j' - j)) + (j - a_i) ^ 2\)

\(f_{i,j} = min(f_{i-1, j'} + c * j' - c * j) + (j - a_i) ^ 2\)

我們發現中間的 \(c * j\) 和取 min 沒有關係,就把它放在外面

\(f_{i,j}=\begin{cases}min(f_{i-1, j'} - c * j') + (j - a_i) ^ 2 + c * j&j' \le j\\min(f_{i-1, j'} + c * j') + (j - a_i) ^ 2 - c * j&j \le j'\end{cases}\)

然後我們發現,前面的那一坨 \(min\) ,其實不需要第一篇題解的單調佇列優化,只需要一邊算一邊取 \(min\)

大概就是一開始設 \(tot = \texttt{INF}\) ,然後遍歷 \(j\)\(1 \le j \le 100\) (或 \(100 \ge j \ge 1\)),每次都更新 \(tot\)\(min(tot, f_{i-1, j} + c * j)\) ,然後注意因為樹的高度只能增加不能減少,所以要注意在 \(j < h_i\) 時不要更新 \(f\) ,直接設為 \(\texttt{INF}\) 然後 continue ,但是 \(tot\) 仍要更新(我就踩了這個坑)。

注意方程的加減號(踩坑 * 2),先把方程捋明白了再寫...

最後求的值就是 \(\min\limits_{i=a_{i}}^{100}f_{n,i}\)

程式碼:

// jaco2567 AK IOI
// #include <bits/stdc++.h>
#include <queue>
#include <stack>
#include <cmath>
#include <string>
#include <cstdio>
#include <iomanip>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;

inline int read() {
    int res = 0, f = 1;
    char k;
    while (!isdigit(k = getchar())) if(k == '-') f = -1;
    while (isdigit(k)) {
        res = res * 10 + k - '0';
        k = getchar();
    }
    return res * f;
}

const int NR = 1e5 + 5;
const int INF = 0x3f3f3f3f;

int n, a[NR], c, f[NR][105], tot;

int main() {
    // freopen(".in", "r", stdin);
    // freopen(".out", "w", stdout);
    
    memset(f, INF, sizeof(f));
    scanf("%d%d", &n, &c);
    for (int i = 1; i <= n; i++) {
        scanf("%d", &a[i]);
    }

    for (int i = 1; i <= 100; i++) {
        if (i >= a[1]) f[1][i] = (i - a[1]) * (i - a[1]);
        else f[1][i] = INF;
    }

    for (int i = 2; i <= n; i++) {
        tot = INF;
        for (int j = 1; j <= 100; j++) {
            tot = min(tot, f[i-1][j] - c * j);
            if (j < a[i]) {
                f[i][j] = INF; 
                continue ;
            }
            f[i][j] = min(f[i][j], tot + c * j + (j - a[i]) * (j - a[i]));
        }

        tot = INF;
        for (int j = 100; j >= 1; j--) {
            tot = min(tot, f[i-1][j] + c * j);
            if (j < a[i]) {
                f[i][j] = INF;
                continue ;
            }
            f[i][j] = min(f[i][j], tot - c * j + (j - a[i]) * (j - a[i]));
            // cout << i << ' ' << j << ' ' << f[i][j] << endl;
        }
    }

    int ans = INF;
    for (int i = 0; i <= 100; i++) {
        ans = min(ans, f[n][i]);
    }
    cout << ans << endl;

    return 0;
}