题意
给你长度为n的序列,可以切m刀将序列切成m+1个子段。每个子段的价值为子段中任意两个数的乘积的总和,让总价值最小。
思路
定义数组
s
[
i
]
=
∑
a
[
i
]
s[i]=\sum a[i]
s[i]=∑a[i],数组
e
[
i
]
=
∑
a
[
i
]
2
e[i]=\sum a[i]^2
e[i]=∑a[i]2。
定义状态
f
[
i
]
[
k
]
f[i][k]
f[i][k]代表前i个数,且k刀的最小价值。可以这样转移
f
[
i
]
[
k
]
=
m
i
n
{
f
[
j
]
[
k
−
1
]
+
1
2
∗
(
(
s
[
i
]
−
s
[
j
]
)
2
−
(
e
[
i
]
−
e
[
j
]
)
)
}
f[i][k]=min\{f[j][k-1]+\frac{1}{2}*((s[i]-s[j])^2 - (e[i]-e[j]))\}
f[i][k]=min{f[j][k−1]+21∗((s[i]−s[j])2−(e[i]−e[j]))}
移项后可得
2
f
[
i
]
[
k
]
−
s
[
i
]
2
+
e
[
i
]
=
2
f
[
j
]
[
k
−
1
]
+
s
[
j
]
2
+
e
[
j
]
−
2
s
[
i
]
s
[
j
]
2f[i][k]-s[i]^2+e[i]=2f[j][k-1]+s[j]^2+e[j]-2s[i]s[j]
2f[i][k]−s[i]2+e[i]=2f[j][k−1]+s[j]2+e[j]−2s[i]s[j]
令
b
=
2
f
[
i
]
[
k
]
−
s
[
i
]
2
+
e
[
i
]
y
=
2
f
[
j
]
[
k
−
1
]
+
s
[
j
]
2
+
e
[
j
]
k
=
2
s
[
i
]
x
=
s
[
j
]
\begin{aligned} &b = 2f[i][k]-s[i]^2+e[i]\\ & y=2f[j][k-1]+s[j]^2+e[j]\\ &k=2s[i]\\ &x=s[j] \end{aligned}
b=2f[i][k]−s[i]2+e[i]y=2f[j][k−1]+s[j]2+e[j]k=2s[i]x=s[j]
转成
y
=
k
x
+
b
y=kx+b
y=kx+b,维护下凸壳。注意需要枚举k。
代码
//
// Created by yjq on 2019/9/5.
//
#include <iostream>
#include <cstring>
using namespace std;
#define ll long long
#define ld long double
#define ull unsigned long long
#define __ ios::sync_with_stdio(0);cin.tie(0);cout.tie(0)
const int maxn = 1510;
int n, m;
ll a[maxn], s[maxn], e[maxn], f[maxn][maxn];
int q[maxn];
ll X(int i, int j, int k) {
return s[j] - s[i];
}
ll Y(int i, int j, int k) {
return (2ll * f[j][k - 1] + s[j] * s[j] + e[j]) - (2ll * f[i][k - 1] + s[i] * s[i] + e[i]);
}
ll DP(int i, int j, int k) {
return f[j][k - 1] + (((s[i] - s[j]) * (s[i] - s[j]) - (e[i] - e[j])) >> 1ll);
}
int main() {
__;
while (cin >> n >> m) {
if (n == 0 && m == 0)break;
memset(s, 0, sizeof s);
memset(f, 0, sizeof f);
memset(e, 0, sizeof e);
for (int i = 1; i <= n; ++i) {
cin >> a[i];
s[i] = s[i - 1] + a[i];
e[i] = e[i - 1] + a[i] * a[i];
}
for (int i = 1; i <= n; ++i) {
f[i][0] = ((s[i] * s[i] - e[i]) >> 1ll);
}
for (int k = 1; k <= m; ++k) {
int l = 0, r = 0;
q[++r] = 0;
for (int i = 1; i <= n; ++i) {
while (l + 1 < r && Y(q[l + 1], q[l + 2ll], k) <=
X(q[l + 1], q[l + 2ll], k) * 2ll * s[i])
++l;
f[i][k] = DP(i, q[l + 1], k);
while (l + 1 < r &&
Y(q[r - 1], q[r], k) * X(q[r], i, k) >=
X(q[r - 1], q[r], k) * Y(q[r], i, k))
--r;
q[++r] = i;
}
}
cout << f[n][m] << endl;
}
return 0;
}