题目描述
P教授要去看奥运,但是他舍不下他的玩具,于是他决定把所有的玩具运到北京。他使用自己的压缩器进行压缩,其可以将任意物品变成一堆,再放到一种特殊的一维容器中。P教授有编号为 1⋯N1⋯N 的 NN 件玩具,第 ii 件玩具经过压缩后变成一维长度为 CiC**i .为了方便整理,P教授要求在一个一维容器中的玩具编号是连续的。同时如果一个一维容器中有多个玩具,那么两件玩具之间要加入一个单位长度的填充物,形式地说如果将第 ii 件玩具到第 jj 个玩具放到一个容器中,那么容器的长度将为 x=j−i+∑k=ijCkx=j−i+k=i∑jCk 制作容器的费用与容器的长度有关,根据教授研究,如果容器长度为 xx ,其制作费用为 (X−L)(X−L)2 .其中 LL 是一个常量。P教授不关心容器的数目,他可以制作出任意长度的容器,甚至超过 LL 。但他希望费用最小.
感谢@ACの666 提供的Latex题面
输入输出格式
输入格式:
第一行输入两个整数N,L.接下来N行输入Ci.1<=N<=50000,1<=L,Ci<=10^7
输出格式:
输出最小费用
输入输出样例
输入样例#1:
5 4
3
4
2
1
4
输出样例#1:
1
思路:
这是一道斜率优化题,我们可以设dp[i]
代表前i个玩具的最小费用值,那么我们可以得到:
dp[i] = min(dp[j] + (i - j - 1 + [j+1, i]的Ck和 - L)^2) (j < i)
我们可以设sum[i]
代表前i个玩具的Ck值的和,于是算式化简为:
dp[i] = min(dp[j] + (i - j - 1 + sum[i] - sum[j] - L)^2) (j < i)
我们继续设不变量A[i] = i - 1 + sum[i] - L
,变量B[j] = j + sum[j]
,那么算式就化简为:
dp[i] = min(dp[j] + (A[i] - B[j])^2)
= min(dp[j] + A[i]^2 + B[j]^2 - 2*A[i]*B[j])
我们看到算式中存在不变量,变量,混合值,因此推断其为斜率优化题,于是我们可以设k < j且 j比k更优,那么:
dp[j] + A[i]^2 + B[j]^2 - 2*A[i]*B[j] < dp[k] + A[i]^2 + B[k]^2 - 2*A[i]*B[k]
最终化简,得到:
((dp[j] + B[j]^2) - (dp[k] + B[k]^2)) / (B[j] - B[k]) < 2 * A[i]
我们再设F[i] = dp[i] + B[i]^2
,于是得:
(F[j] - f[k]) / (B[j] - B[k]) < 2 * A[i]
我们得到了斜率,根据斜率优化的基操,剩下的通用步骤就不一一赘述,直接上代码
代码:
#include <stdio.h>
#define ll long long
#define N 50000
ll sum[N + 5] = {0};
ll A[N + 5] = {0};
ll B[N + 5] = {0};
ll F[N + 5] = {0};
ll dp[N + 5] = {0};
int q[N + 5] = {0};
int head = 0, tail = 0;
double get(int j, int k) {
return 1.0 * (F[j] - F[k]) / (B[j] - B[k]);
}
void set(int i, int j) {
dp[i] = dp[j] + (A[i] - B[j]) * (A[i] - B[j]);
F[i] = dp[i] + B[i] * B[i];
}
int main () {
int n, L, a;
scanf("%d%d", &n, &L);
for (int i = 1; i <= n; i++) {
scanf("%d", &a);
sum[i] = sum[i - 1] + a;
A[i] = i - 1 + sum[i] - L;
B[i] = i + sum[i];
}
q[tail++] = 0;
q[tail++] = 1;
set(1, 0);
for (int i = 2; i <= n; i++) {
while(tail - head > 1 && get(q[head + 1], q[head]) <= 2 * A[i]) {
head++;
}
set(i, q[head]);
while(tail - head > 1 && get(i, q[tail - 1]) <= get(q[tail - 1], q[tail - 2])) {
tail--;
}
q[tail++] = i;
}
printf("%lld\n", dp[n]);
return 0;
}
如果有写的不对或者不全面的地方 可通过主页的联系方式进行指正,谢谢