1 条题解

  • 0
    @ 2025-8-24 22:10:37

    自动搬运

    查看原文

    来自洛谷,原作者为

    avatar fighter
    **

    搬运于2025-08-24 22:10:37,当前版本为作者最后更新于2019-06-13 15:29:40,作者可能在搬运后再次修改,您可在原文处查看最新版

    自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多

    以下是正文


    首先有一个非常显然的分段式DP:

    f[i][j]f[i][j]表示前ii条蛇,改变jj次大小所得到的最小剩余空间

    g[i][j]g[i][j]表示区间[i,j][i,j]的空余空间

    那么可以得到如下转移方程:

    f[i][j]=min(f[i][j],f[k][j1]+g[k+1][i])f[i][j] = min(f[i][j], f[k][j-1]+g[k+1][i])

    kk为我们枚举的一个断点,表示[k+1,i][k+1,i]这段区间用同一大小的网。

    那么考虑怎么求g[i][j]g[i][j]

    根据定义,一段区间的空余空间一定是用区间中的最大值作为网的大小,减去其他数字得到的和。即g[i][j]=maxn(ji+1)sum(i,j)g[i][j]=maxn*(j-i+1)-sum(i,j),其中区间和可以用前缀和O(1)O(1)处理。

    由于我们的dpdp已经是O(n3)O(n^3)了,所以我们考虑快速(最好是O(1)O(1))求出区间最大值。

    首先想到的是线段树,但有一只loglog,太慢了。然后发现没有修改操作,可以用ststO(1)O(1)查询搞一搞(没写过不知道效果怎么样)。

    但实际上有一种更简洁的方式,由于我们枚举断点的时候会从i1i-1向下逐一枚举,那么我们可以在枚举断点的时候同时更新最大值。

    #include <bits/stdc++.h>
    #define MAX 405
    using namespace std;
    
    int n, m;
    int a[MAX], f[MAX][MAX], s[MAX];
    
    int main()
    {
        cin >> n >> m;
        for (int i = 1; i <= n; ++i) {
            scanf("%d", &a[i]);
            s[i] = s[i-1]+a[i];
        }
        m++;
        memset(f, 0x3f, sizeof(f));
        f[0][0] = 0;
        for (int i = 1; i <= n; ++i) {
            for (int j = 1; j <= min(m, i); ++j) {
                int mx = a[i];
                for (int k = i-1; k >= 0; --k) {
                    f[i][j] = min(f[i][j], f[k][j-1]+mx*(i-k)-(s[i]-s[k]));
                    mx = max(mx, a[k]);
                }
            }
        }
        int ans = 0x3f3f3f3f;
        for (int i = 0; i <= m; ++i) {
            ans = min(ans, f[n][i]);
        }
        cout << ans << endl;
    
        return 0;
    }
    
    • 1

    信息

    ID
    4402
    时间
    2000ms
    内存
    256MiB
    难度
    4
    标签
    递交数
    0
    已通过
    0
    上传者