习题:玩具装箱(斜率优化)
题目:
传送门
思路:
首先考虑暴力,那么设dp[i]为以前i号元素的最小值
s[i]为前i号元素的前缀和
那么转移方程即为dp[i]=min{ dp[j]+(i-j-1+s[i]-s[j]-l)^2 }
显然朴素的时间复杂度为O(n^2)
那么我们化简一下式子
设有两个点a,b向i进行转移,且a比b优
那么dp[a]+(i-a-1+s[i]-s[a]-l)^2<dp[b]+(i-b-1+s[i]-s[b]-l)^2
推导过程此处省略
设x[i]=s[i]+1,y[i]=(x[i]+l+1)^2
2*y[i]>=(dp[a]+x[a]-dp[b]-x[b])/(y[a]-y[b])
以此为基础,这道题的代码就很容易码出来了
#include<iostream>
using namespace std;
long long n,l;
long long dp[50005];
long long s[50005];
long long head;
long long tail;
long long x[50005];
long long y[50005];
long long q[50005];
double check_=1;
long long pf(long long x)
{
return x*x;
}
double get_k(long long a,long long b)
{
return check_*(dp[b]+y[b]-dp[a]-y[a])/(x[b]-x[a]);
}
int main()
{
cin>>n>>l;
for(int i=1;i<=n;i++)
{
cin>>s[i];
s[i]+=s[i-1];
x[i]=s[i]+i;
y[i]=pf(x[i]+l+1);
}
y[0]=pf(l+1);
for(int i=1;i<=n;i++)
{
while(head<tail)
{
if(get_k(q[head],q[head+1])<=2*x[i])
head++;
else
break;
}
dp[i]=dp[q[head]]+pf(x[i]-x[q[head]]-l-1);
while(head<tail)
{
if(get_k(q[tail],i)<get_k(q[tail-1],q[tail]))
tail--;
else
break;
}
q[++tail]=i;
}
cout<<dp[n];
return 0;
}

浙公网安备 33010602011771号