• 【HNOI2008】玩具装箱


    题目链接

    【HNOI2008】玩具装箱

    题目描述

    P教授要去看奥运,但是他舍不下他的玩具,于是他决定把所有的玩具运到北京。他使用自己的压缩器进行压缩,其可以将任意物品变成一堆,再放到一种特殊的一维容器中。
    P教授有编号为(1 cdots n)(n)件玩具,第(i)件玩具经过压缩后的一维长度为(C_i)
    为了方便整理,P教授要求:

    • 在一个一维容器中的玩具编号是连续的。
    • 同时如果一个一维容器中有多个玩具,那么两件玩具之间要加入一个单位长度的填充物。形式地说,如果将第(i)件玩具到第(j)个玩具放到一个容器中,那么容器的长度将为(x=j-i+sumlimits_{k=i}^{j}C_k)

    制作容器的费用与容器的长度有关,根据教授研究,如果容器长度为(x),其制作费用为((x-L)^2)。其中(L)是一个常量。P教授不关心容器的数目,他可以制作出任意长度的容器,甚至超过(L)。但他希望所有容器的总费用最小。

    输入格式

    第一行有两个整数,用一个空格隔开,分别代表(n)(L)
    (2)到第((n+1))行,每行一个整数,第((i+1))行的整数代表第(i)件玩具的长度(C_i)

    输出格式

    输出一行一个整数,代表所有容器的总费用最小是多少。

    样例输入

    5 4
    3
    4
    2
    1
    4
    

    样例输出

    1

    说明/提示

    对于全部的测试点(1 leq n leq 5 imes 10^4)(1 leq L leq 10^7)(1 leq C_i leq 10^7)

    题解

    题意:有(n)个物品,每个物品都有长度(C_i),要求吧每个物品装进任意个箱子里使花费最小,给定(L)
    花费计算公式如下:
    (x=j-i+sumlimits_{k=i}^{j}C_k)
    (ans=(x-L)^2)
    看到这里我们首先就想到dp。
    转移方程为:
    以下(i)均表示(i)(j)装一个箱子
    (dp[j]=min(dp[i-1]+(x(j,i)-l)^2))
    (i le j)
    (x(j,i)=s[j]-s[i-1]+j-i)
    (s[i])表示(sumlimits_{k=1}^{i}C_k)
    那么这样计算的话时间复杂度就是(O(n^2))的,显然过不了此题。
    那么我们考虑到用斜率优化。
    我们假设有(k,i),满足(k lt i le j),且(i)的决策笔(k)的决策优,
    那么(k,i)肯定满足:
    (dp[i-1]+(s[j]+j-l-(s[i-1]+i))^2<dp[k-1]+(s[k]+k-l-(s[k-1]+k))^2)
    我们化简一下上面的式子:
    我们令:
    (a=s[j]+j-l)
    (b[i]=s[i-1]+i)
    那么原式化简后就是:
    (frac{(dp[i-1]+b[i]^2)-(dp[k-1]+b[k]^2)}{(b[i]-b[k])}<2a)
    因为对于每个确定的(j)来说(a)是常数,所以就可以用斜率优化来优化我们的dp了。
    用凸包来保证dp的时间复杂度,具体实现看代码。
    上代码:
    注:代码中i表示i到j装一个箱子,k同上。

    #include<bits/stdc++.h>
    using namespace std;
    int n,L;
    int c[50009];
    long long dp[50009],s[500009];
    int pp[50009],l,r;
    long long js(int i,int k){return 1.0*((dp[i-1]+(s[i-1]+i)*(s[i-1]+i))-(dp[k-1]+(s[k-1]+k)*(s[k-1]+k)))/(s[i-1]+i-s[k-1]-k);}
    int main(){
    	scanf("%d%d",&n,&L);
    	for(int j=1;j<=n;j++){
    		scanf("%d",&c[j]);
    		s[j]=s[j-1]+c[j];
    	}
    	pp[0]=1;
    	for(int j=1;j<=n;j++){
    		while(l<r && js(pp[l+1],pp[l])<2*(s[j]+j-L)) l++;
    		dp[j]=dp[pp[l]-1]+(s[j]-s[pp[l]-1]+j-pp[l]-L)*(s[j]-s[pp[l]-1]+j-pp[l]-L);
    		while(l<r && js(pp[r],pp[r-1])>js(j+1,pp[r])) r--;
    		pp[++r]=j+1;
    	}
    	printf("%lld",dp[n]);
    	return 0;
    }
    
  • 相关阅读:
    初识分布式计算:从MapReduce到Yarn&Fuxi
    日志的艺术(The art of logging)
    关于负载均衡的一切:总结与思考
    打印中文dict list的各种姿势
    不想再被鄙视?那就看进来! 一文搞懂Python2字符编码
    什么是分布式系统,如何学习分布式系统
    再论分布式事务:从理论到实践
    从银行转账失败到分布式事务:总结与思考
    git命令
    IntelliJ idea 中使用Git
  • 原文地址:https://www.cnblogs.com/linjiale/p/12819508.html
Copyright © 2020-2023  润新知