P3195 [HNOI2008]玩具装箱 (斜率优化dp)

题目链接:点击这里

题目大意:
P P P 教授要去看奥运,但是他舍不下他的玩具,于是他决定把所有的玩具运到北京。他使用自己的压缩器进行压缩,其可以将任意物品变成一堆,再放到一种特殊的一维容器中。
P P P 教授有编号为 1 ⋯ n 1 \cdots n 1n n n n 件玩具,第 i i i 件玩具经过压缩后的一维长度为 C i C_i Ci
为了方便整理, P P P教授要求:
1.在一个一维容器中的玩具编号是连续的。
2.同时如果一个一维容器中有多个玩具,那么两件玩具之间要加入一个单位长度的填充物。形式地说,如果将第 i i i 件玩具到第 j j j 个玩具放到一个容器中,那么容器的长度将为 x = j − i + ∑ k = i j C k x=j-i+\sum\limits_{k=i}^{j}C_k x=ji+k=ijCk
制作容器的费用与容器的长度有关,根据教授研究,如果容器长度为 xx,其制作费用为 ( x − L ) 2 (x-L)^2 (xL)2 。其中 LL 是一个常量。 P P P 教授不关心容器的数目,他可以制作出任意长度的容器,甚至超过 L L L 。但他希望所有容器的总费用最小。

题目分析:
d p [ i ] dp[i] dp[i] 为前 i i i 个装箱的最小花费
故有转移方程: d p [ i ] = m i n j = 0 i − 1 ( d p [ j ] + ( s u m [ i ] + i − s u m [ j ] − j − L − 1 ) 2 ) dp[i]=min_{j=0}^{i-1}(dp[j]+(sum[i]+i−sum[j]−j−L−1) ^2) dp[i]=minj=0i1(dp[j]+(sum[i]+isum[j]jL1)2)
其中 s u m [ i ] = ∑ j = 1 i C i sum[i]=\sum_{j=1}^iC_i sum[i]=j=1iCi
这个方程的时间复杂度是 O ( n 2 ) O(n^2) O(n2)
我们考虑优化:
斜率优化:
G [ i , j ] G[i,j] G[i,j] d p [ i ] dp[i] dp[i] 的一个决策点,即 G [ i , j ] = d p [ j ] + ( s u m [ i ] + i − s u m [ j ] − j − L − 1 ) 2 G[i,j]=dp[j]+(sum[i]+i−sum[j]−j−L−1) ^2 G[i,j]=dp[j]+(sum[i]+isum[j]jL1)2
F [ x ] = s u m [ x ] + x , S = L + 1 F[x]=sum[x]+x,S=L+1 F[x]=sum[x]+x,S=L+1
G [ i , j ] = d p [ j ] + ( F [ i ] − F [ j ] − S ) 2 G[i,j]=dp[j]+(F[i]-F[j]-S)^2 G[i,j]=dp[j]+(F[i]F[j]S)2
= d p [ j ] + F [ i ] 2 − 2 F [ i ] ∗ S − 2 F [ i ] ∗ F [ j ] + ( F [ j ] + S ) 2 =dp[j]+F[i]^2-2F[i]*S-2F[i]*F[j]+(F[j]+S)^2 =dp[j]+F[i]22F[i]S2F[i]F[j]+(F[j]+S)2
a , b a,b a,b 为为 d p [ i ] dp[i] dp[i] 的两个决策点且 b b b 点比 a a a 点更优(不妨令 a < b a<b a<b),则有 G [ i , a ] > G [ i , b ] G[i,a]>G[i,b] G[i,a]>G[i,b]
d p [ a ] + F [ i ] 2 − 2 F [ i ] ∗ S − 2 F [ i ] ∗ F [ a ] + ( F [ a ] + S ) 2 > d p [ b ] + F [ i ] 2 − 2 F [ i ] ∗ S − 2 F [ i ] ∗ F [ b ] + ( F [ b ] + S ) 2 dp[a]+F[i]^2-2F[i]*S-2F[i]*F[a]+(F[a]+S)^2>dp[b]+F[i]^2-2F[i]*S-2F[i]*F[b]+(F[b]+S)^2 dp[a]+F[i]22F[i]S2F[i]F[a]+(F[a]+S)2>dp[b]+F[i]22F[i]S2F[i]F[b]+(F[b]+S)2
等价于 F [ i ] ∗ ( F [ b ] − F [ a ] ) > d p [ b ] + ( F [ b ] + S ) 2 − d p [ a ] − ( F [ a ] + S ) 2 F[i]*(F[b]-F[a])>dp[b]+(F[b]+S)^2-dp[a]-(F[a]+S)^2 F[i](F[b]F[a])>dp[b]+(F[b]+S)2dp[a](F[a]+S)2
等价于 F [ i ] > d p [ b ] + ( F [ b ] + S ) 2 − d p [ a ] − ( F [ a ] + S ) 2 F [ b ] − F [ a ] F[i]>\frac{dp[b]+(F[b]+S)^2-dp[a]-(F[a]+S)^2}{F[b]-F[a]} F[i]>F[b]F[a]dp[b]+(F[b]+S)2dp[a](F[a]+S)2 (因为 F [ x ] = s u m [ x ] + x F[x]=sum[x]+x F[x]=sum[x]+x 所以 F [ b ] − F [ a ] > 0 F[b]-F[a]>0 F[b]F[a]>0
Y [ x ] = d p [ x ] + ( F [ x ] + S ) 2 Y[x]=dp[x]+(F[x]+S)^2 Y[x]=dp[x]+(F[x]+S)2
故原式等价于 F [ i ] > Y [ b ] − Y [ a ] F [ b ] − F [ a ] F[i]>\frac{Y[b]-Y[a]}{F[b]-F[a]} F[i]>F[b]F[a]Y[b]Y[a] F [ i ] F[i] F[i] 显然是一个单调递增的函数)
有了这个式子我们就可以通过维护一个下凸包来进行斜率优化了(斜率小转移点的更优秀,推倒过程可以参照这篇博客点击这里

具体细节见代码:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<vector>
#define ll long long
#define inf 0x3f3f3f3f
using namespace std;
ll read()
{
	ll res = 0,flag = 1;
	char ch = getchar();
	while(ch<'0' || ch>'9')
	{
		if(ch == '-') flag = -1;
		ch = getchar();
	}
	while(ch>='0' && ch<='9')
	{
		res = (res<<3)+(res<<1)+(ch^48);//res*10+ch-'0';
		ch = getchar();
	}
	return res*flag;
}
const int maxn = 1e5+5;
const int mod = 1e9+7;
const double pi = acos(-1);
const double eps = 1e-8;
ll n,s,f[maxn],dp[maxn],qu[maxn],h,t;
ll Y(int x)
{
	return dp[x]+(f[x]+s)*(f[x]+s);
}
double calc(int i,int j)
{
	return (double)(Y(i)-Y(j))/(f[i]-f[j]);
}
int main()
{
	n = read(),s = read();
	s++;//处理 S 
	for(int i = 1;i <= n;i++)
	{
		f[i] = read();
		f[i] += f[i-1]+1;//处理 F[x]数组 
	}
	h = t = 1;ll j;
	for(int i = 1;i <= n;i++)
	{
		while(h < t && calc(qu[h],qu[h+1]) <= 2*f[i]) h++;
		int j = qu[h];
		dp[i] = dp[j]+(f[i]-f[j]-s)*(f[i]-f[j]-s);
		while(h < t && calc(qu[t-1],qu[t]) >= calc(qu[t-1],i)) t--;
		qu[++t] = i;
	}
	printf("%lld\n",dp[n]);
	return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值