洛谷P3195玩具装箱TOY

题目描述

P教授要去看奥运,但是他舍不下他的玩具,于是他决定把所有的玩具运到北京。他使用自己的压缩器进行压缩,其可以将任意物品变成一堆,再放到一种特殊的一维容器中。P教授有编号为 1⋯N1⋯N 的 NN 件玩具,第 ii 件玩具经过压缩后变成一维长度为 CiC**i .为了方便整理,P教授要求在一个一维容器中的玩具编号是连续的。同时如果一个一维容器中有多个玩具,那么两件玩具之间要加入一个单位长度的填充物,形式地说如果将第 ii 件玩具到第 jj 个玩具放到一个容器中,那么容器的长度将为 x=j−i+∑k=ijCkx=ji+k=ijCk 制作容器的费用与容器的长度有关,根据教授研究,如果容器长度为 xx ,其制作费用为 (X−L)(XL)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;
}

转载请注明出处!!!

如果有写的不对或者不全面的地方 可通过主页的联系方式进行指正,谢谢

猜你喜欢

转载自blog.csdn.net/Ivan_zcy/article/details/88137586
今日推荐