洛谷 P2885 题解

教练(校外)给我讲了这道题后觉得很有必要记录一下...

明显有个很明显的暴力(废话)

\(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;
}
posted @ 2021-11-06 11:57  MilkyCoffee  阅读(99)  评论(1)    收藏  举报