hdu3507
题意
给出 N 个数字,输出的时候可以选择连续的输出,每连续输出一串,它的费用是 这串数字和的平方加上一个常数 M。
分析
斜率优化dp,入门题。
得到 dp 方程后,发现是O(n * n)的复杂度,且 n 很大,考虑用斜率优化。
设 s[i] 为 1 到 i 的花费之和。
dp方程:(dp[i] = dp[j] + (s[i] - s[j])^2 + m ( i > j))
如果选 j 比选 k 更优,则有 (dp[j] + (s[i] - s[j])^2 + m < dp[k] + (s[i] - s[k])^2 + m)
移项可得, $(dp[j] + s[j]^2 - (dp[k] + s[k]^2)) / (2 * (s[j] - s[k])) < s[i] $
这样形成类似于求斜率的式子。
那么满足这个式子,是由于前提条件:j 比 k 更优,即选 j 花费更少。
令 $ g[j, l] = (dp[l] + s[l]^2 - (dp[j] + s[j]^2)) / (2 * (s[l] - s[j]))$
code
#include<cstdio>
using namespace std;
const int MAXN = 5e5 + 5;
int s[MAXN], q[MAXN], dp[MAXN];
int n, m;
int up(int k, int j) {
return dp[j] + s[j] * s[j] - (dp[k] + s[k] * s[k]);
}
int down(int k, int j) {
return 2 * (s[j] - s[k]);
}
int get(int i, int j) {
return dp[j] + (s[i] - s[j]) * (s[i] - s[j]) + m;
}
int main() {
while(~scanf("%d%d", &n, &m)) {
s[0] = 0;
dp[0] = 0;
for(int i = 1; i <= n; i++) {
scanf("%d", &s[i]);
s[i] += s[i - 1];
}
int head = 0, tail = 0;
q[tail++] = 0;
for(int i = 1; i <= n; i++) {
while(head + 1 < tail && up(q[head], q[head + 1]) <= s[i] * down(q[head], q[head + 1]))
head++;
dp[i] = get(i, q[head]);
// g[k,j] >= g[j,l] (k < j < l) 说明 j 可以舍去
while(head + 1 < tail && up(q[tail - 2], q[tail - 1]) * down(q[tail - 1], i) >= up(q[tail - 1], i) * down(q[tail - 2], q[tail - 1]))
tail--;
q[tail++] = i;
}
printf("%d
", dp[n]);
}
return 0;
}