P3195 玩具装箱TOY——斜率优化

原题见洛谷对应题号的题面。

分析这道题,我们能得到一个初步的DP方程,即:
f [ i ] = m i n { f [ j ] + ( Σ j + 1 i c i + i − j − 1 − L ) 2 } , 1 < = j < i f[i]=min\{f[j]+(\Sigma^i_{j+1}c_i+i-j-1-L)^2\},1<=j<i f[i]=min{f[j]+(Σj+1ici+ij1L)2},1<=j<i
计算这个方程的复杂度显然是 O ( n 3 ) O(n^3) O(n3)的,因此我们需要对其进行优化。

s [ i ] s[i] s[i] c [ i ] c[i] c[i]的前缀和数组, m ( i ) = s [ i ] + i m(i)=s[i]+i m(i)=s[i]+i,为了书写方便,去掉min,则我们可以把上面的方程改写为这样的形式:
f [ i ] = f [ j ] + ( s [ i ] − s [ j ] + i − j − 1 − L ) 2 f[i]=f[j]+(s[i]-s[j]+i-j-1-L)^2 f[i]=f[j]+(s[i]s[j]+ij1L)2
然后有:
f [ i ] = f [ j ] + [ ( m ( i ) − m ( j ) ) − ( L + 1 ) ] 2 f[i]=f[j]+[(m(i)-m(j))-(L+1)]^2 f[i]=f[j]+[(m(i)m(j))(L+1)]2
f [ i ] = f [ j ] + m ( i ) 2 + m ( j ) 2 − 2 ∗ m ( i ) ∗ m ( j ) + ( L + 1 ) 2 − 2 ∗ ( m ( i ) − m ( j ) ) ∗ ( L + 1 ) f[i]=f[j]+m(i)^2+m(j)^2-2*m(i)*m(j)+(L+1)^2-2*(m(i)-m(j))*(L+1) f[i]=f[j]+m(i)2+m(j)22m(i)m(j)+(L+1)22(m(i)m(j))(L+1)
f [ i ] = f [ j ] + m ( j ) 2 + m ( i ) 2 − 2 ∗ m ( i ) ∗ m ( j ) − 2 ∗ ( L + 1 ) ∗ m ( i ) + 2 ∗ ( L + 1 ) ∗ m ( j ) + ( L + 1 ) 2 f[i]=f[j]+m(j)^2+m(i)^2-2*m(i)*m(j)-2*(L+1)*m(i)+2*(L+1)*m(j)+(L+1)^2 f[i]=f[j]+m(j)2+m(i)22m(i)m(j)2(L+1)m(i)+2(L+1)m(j)+(L+1)2
f [ i ] = f [ j ] + m ( j ) 2 + [ − 2 ∗ m ( i ) + 2 ∗ L + 2 ] ∗ m ( j ) + [ m ( i ) − 2 ∗ L − 2 ] ∗ m ( i ) + ( L + 1 ) 2 f[i]=f[j]+m(j)^2+[-2*m(i)+2*L+2]*m(j)+[m(i)-2*L-2]*m(i)+(L+1)^2 f[i]=f[j]+m(j)2+[2m(i)+2L+2]m(j)+[m(i)2L2]m(i)+(L+1)2
移项,有:
f [ j ] + m ( j ) 2 = [ 2 ∗ m ( i ) − 2 ∗ L − 2 ] ∗ m ( j ) + [ 2 ∗ L + 2 − m ( i ) ] ∗ m ( i ) − ( L + 1 ) 2 + f [ i ] f[j]+m(j)^2=[2*m(i)-2*L-2]*m(j)+[2*L+2-m(i)]*m(i)-(L+1)^2+f[i] f[j]+m(j)2=[2m(i)2L2]m(j)+[2L+2m(i)]m(i)(L+1)2+f[i]
至此,我们观察这个式子,可以发现,设:
f [ j ] + m ( j ) 2 = y , [ 2 ∗ m ( i ) − 2 ∗ L − 2 ] = k , m ( j ) = x , [ 2 ∗ L + 2 − m ( i ) ] ∗ m ( i ) − ( L + 1 ) 2 + f [ i ] = b f[j]+m(j)^2=y,[2*m(i)-2*L-2]=k,m(j)=x,[2*L+2-m(i)]*m(i)-(L+1)^2+f[i]=b f[j]+m(j)2=y,[2m(i)2L2]=k,m(j)=x,[2L+2m(i)]m(i)(L+1)2+f[i]=b
则有 y = k x + b y=kx+b y=kx+b,其中 b b b 含有我们需要求解的 f [ i ] f[i] f[i]

我们的目的是让 f [ i ] f[i] f[i]取得最小值,则 b b b 就应取得最小值。我们可以将这个问题转换成直线在平面直角坐标系内的纵截距最小的问题。这样,这个问题就与线性规划问题非常相似了。

在枚举 i i i 时,我们每次都能得到一个固定的斜率 k k k (在后面称为“标准斜率”),标准斜率即为在 i = k i=k i=k 时,理想状态下的答案,且斜率小于它的直线是不合法,也就是在现在的条件下无法成立的。因此,我们可以维护一个 k k k 值单调递减的单调队列,每次将不合法的斜率出队,取比标准斜率大的第一个斜率,也就是队头,作为计算答案时使用的斜率。同时,检查入队的点与在它之前的点组成的斜率的关系,来判断队尾的点是否应该出队。
在这里插入图片描述
如图, k k k 为标准斜率, k 1 k_1 k1 k 2 k_2 k2 为“入队的点与在它之前的点组成的斜率”,入队的点是所对应的 x x x 值最大的点。在左图中, k 1 > k 2 k_1>k_2 k1>k2 ,所以 k 1 k_1 k1 仍然是最优解,而 k 2 k_2 k2 虽然没有 k 1 k_1 k1 优,但是组成它的点在组成 k 1 k_1 k1 的点之后,仍然有成为最优解的潜力。在右图中, k 1 < k 2 k_1<k_2 k1<k2 ,因此在 k 1 k_1 k1 k 2 k_2 k2 中间的点一定不可能参与构成最优解,因为 k 3 k_3 k3 一定比 k 2 k_2 k2 更优。这时,我们就可以删除在 k 1 k_1 k1 k 2 k_2 k2 中间的点,也就是上面提到的“出队”,之后再把现在所需的点入队。

这样,一定能保证每次计算出的答案都是最优的。

关于两点之间的斜率,我们可以直接使用斜率公式 ( y 1 − y 2 ) / ( x 1 − x 2 ) (y_1-y_2)/(x_1-x_2) (y1y2)/(x1x2)来求解,并与标准斜率作比较。

AC代码:

#include<cstdio>
#define ll long long
using namespace std;
ll n,l,q[50005],tail=1,head=1,f[50005],s[50005];
ll m(ll j) {return s[j]+j;}
ll y(ll j) {return f[j]+m(j)*m(j);}
ll k(ll i) {return 2*m(i)-2*l-2;}
long double nk(ll i,ll j) {return (y(i)-y(j))/(m(i)-m(j));}
ll b(ll i) {return m(i)*(2*l+2-m(i))-(l+1)*(l+1);}
int main()
{
	scanf("%lld%lld",&n,&l);
	for(int i=1;i<=n;i++)
	{
		scanf("%lld",&s[i]);
		s[i]+=s[i-1];
	}
	for(int i=1;i<=n;i++)
	{
		while(head<tail&&k(i)>nk(q[head],q[head+1])) head++;
		f[i]=y(q[head])-k(i)*m(q[head])-b(i);
		while(head<tail&&nk(q[tail-1],i)<nk(q[tail-1],q[tail])) tail--;
		q[++tail]=i;
	}
	printf("%lld",f[n]);
	return 0;
} 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值