首页 > 其他 > 详细

[HDU 2829]Lawrence

时间:2020-08-06 01:34:26      阅读:97      评论:0      收藏:0      [点我收藏+]

Description

题库链接

给你一个长度为 \(n\) 的数组 \(a\)。你需要将其划分为 \(m+1\) 段,每一段的贡献为该段内所有元素两两乘积的和。求所有划分中贡献最少时的贡献。

\(1\leq m< n\leq 1000\)

Solution

假设前 \(i\) 个数分为 \(j\) 段的最少贡献为 \(f_{i,j}\),那么可以列出 DP 方程 \(f_{i,j}=\min\{f_{k-1,j-1}+w(k, i)\}\)

这里的 \(w\) 其实就是这一段所有元素的和平方减去平方和再除以 2。显然 \(w\) 是满足四边形不等式的。那么可以用四边形不等式定理优化 DP,具体可以参考[IOI 2000]邮局这题题解。时间复杂度为 \(O(n^2)\)

由于 \(w\) 是一个二次函数形式的式子,也可以斜率优化,复杂度是 \(O(nm)\) 的。

Code

#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int N = 1005;

int n, m, a[N], s[N][N];
ll f[N][N], sum[N];

int main() {
    while (~scanf("%d%d", &n, &m) && (n || m)) {
        for (int i = 1; i <= n; i++) {
            scanf("%d", &a[i]);
            sum[i] = sum[i-1]+1ll*a[i]*a[i];
            a[i] += a[i-1];
        }
        for (int i = 0; i <= n; i++) {
            s[i][0] = 1;
            f[i][0] = (1ll*a[i]*a[i]-sum[i])/2;
            for (int j = 1; j <= m; j++)
                f[i][j] = 1e12;
        }
        for (int i = 1; i <= m; i++) s[n+1][i] = n;
        f[0][0] = 0;
        for (int j = 1; j <= m; j++)
            for (int i = n; i >= 1; i--)
                for (int k = s[i][j-1]; k <= s[i+1][j]; k++)
                    if (f[i][j] > f[k-1][j-1]+(1ll*(a[i]-a[k-1])*(a[i]-a[k-1])-sum[i]+sum[k-1])/2) {
                        f[i][j] = f[k-1][j-1]+(1ll*(a[i]-a[k-1])*(a[i]-a[k-1])-sum[i]+sum[k-1])/2;
                        s[i][j] = k;
                    }
        printf("%lld\n", f[n][m]);
    }
    return 0;
}

[HDU 2829]Lawrence

原文:https://www.cnblogs.com/NaVi-Awson/p/13443785.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!