[HNOI2008]玩具装箱TOY

洛谷题目链接

动态规划\(+\)单调队列\(+\)斜率优化

身为\(dp\)蒟蒻的我,准备学斜率优化和单调队列,自然不能放过这个经典题目了,我们对于这题很容易想出一个暴力式子:

\(sum[i]\)为前缀和)

\(f[i]=min(f[i],f[j]+(sum[i]-sum[j]+i-j-L-1)^2)\)

我们不考虑最小值,把\(sum[i]+i\)\(a[i]\)代替,\(sum[j]+j+L+1\)\(b[j]\)代替,那么式子整理一下如下:

\(f[i]=f[j]+(a[i]+b[j])^2\)

去括号:

\(f[i]=f[j]+a[i]^2+2a[i]b[j]+b[j]^2\)

移项:

\(f[j]+b[j]^2=2a[i]b[j]+f[i]+a[i]^2\)

\(y=f[j]+b[j]^2\)\(x=b[j]\)\(b=f[i]+a[i]^2\),这就很像一次函数了,那么斜率自然就是\(k=2a[i]\)

为什么这么设呢,因为我们发现\(y,x,k\)在知道\(i,j\)时可以快速求出

\(f[i]\)的含义转化为:当上述直线过点\(P(b[j],f[j]+b[j]^2)\)时,直线在y轴的截距减去\(a[i]^2\)(一个定值)

而题目即为找这个截距的最小值

而且我们仔细一看发现会有单调性,于是单调队列维护最小值

点击查看代码
// luogu-judger-enable-o2
#include<iostream>
#include<cstdio>
#define N 50007
#define int long long
using namespace std;
int n,L;
int sum[N],que[N],f[N];
int A(int i)
{
	return sum[i]+i;
}
int B(int i)
{
	return sum[i]+i+1+L;
}
int X(int i)
{
	return B(i);
}
int Y(int i)
{
	return f[i]+B(i)*B(i);
}
int Get_k(int i,int j)
{
	return (Y(i)-Y(j))/(X(i)-X(j));
}
signed main()
{
	scanf("%lld%lld",&n,&L);
	for(int i=1;i<=n;++i)
	{
		scanf("%lld",&sum[i]);
		sum[i]+=sum[i-1];
	}
	int head=1,tail=1;
	for(int i=1;i<=n;++i)
	{
		while(head<tail&&Get_k(que[head],que[head+1])<2*A(i))
			++head;
		f[i]=f[que[head]]+(A(i)-B(que[head]))*(A(i)-B(que[head]));
		while(head<tail&&Get_k(que[tail],que[tail-1])>Get_k(i,que[tail-1]))
			--tail;
		que[++tail]=i;
	}
	printf("%lld",f[n]);
	return 0;
}

  
posted @ 2019-01-03 21:16  模拟退火  阅读(155)  评论(0)    收藏  举报