HDU3507打印文章 斜率优化入门

该博客介绍了如何使用斜率优化法解决HDU3507打印文章的问题。数据范围为n≤500000, m≤1000。博主首先阐述了题目要求,即输出数字序列的最小费用计算。接着,通过建立动态规划方程并简化,引入斜率优化的概念,解释了如何利用单调队列和凸包来降低时间复杂度到O(n log n)。博客主要作为斜率优化的入门教程,并提示后续会针对更复杂的题目进行深入探讨。" 135767630,9071370,理解Servlet API:ServletRequest与ServletResponse接口,"['servlet', 'javax.servlet', 'HTTP']

打印文章

一、题意及数据范围

题目描述
题目大意:输出N个数字a[N],输出的时候可以连续的输出,每连续输出一串,它的费用是 “这串数字和的平方加上一个常数M”,求最小的费用。
数据范围
n<=500000 , m<=1000。

二、解法

基本思路

根据题目,我们可以列出dp[i]=dp[j]+(a[i]-a[j])2+m,其中dp[i]表示i点是最后一段的最后一个数字的最小花费,a[j]是前缀和。但是这个dp式显然是O(n2)的,我们考虑怎么对它进行优化,化简得:
dp[i]=dp[j]+a[i]2+a[j]2-2*a[i]*a[j]+m
由于这个dp式在计算时需要a[i]和a[j]的乘积,单调队列就不能维护了。

斜率优化
半年多没接触,都不知道怎么用它了……
接下来有一些玄学推导:
设用j更新比k更优,则:
dp[j]+a[i]2+a[j]2-2*a[i]*a[j]+m<dp[k]+a[i]2+a[k]2-2*a[i]*a[k]
消去同类项:dp[j]+a[j]2-2*a[i]*a[j]<dp[k]+a[k]2-2*a[i]*a[k]
移项:dp[j]-dp[k]+a[j]2-a[k]2<2*a[i]*(a[j]-a[k])
我们设f[j]=dp[j]+a[j]2,f[k]=dp[k]+a[k]2
dp[j]−a[k]a[j]−a[k]\frac{dp[j]-a[k]}{a[j]-a[k]}a[j]a[k]dp[j]a[k]<2*a[i]
这个东西……是不是很像斜率
哈!这就是它为什么要叫斜率优化。

具体实现
其实最重要的东西我都讲了,我们可以用这个式子结合单调队列维护凸包,就能过了。
看代码吧。

#include <cstdio>
#define LL long long 
const LL MAXN = 500005;
LL read()
{
	LL x=0,flag=1;char c;
	while((c=getchar())<'0' || c>'9') if(c=='-') flag=-1;
	while(c>='0' && c<='9') x=(x<<3)+(x<<1)+c-'0',c=getchar();
	return x*flag;
}
LL n,m,a[MAXN],q[MAXN],dp[MAXN];

LL slope_up(LL j,LL k)
{
	return (dp[j]+a[j]*a[j])-(dp[k]+a[k]*a[k]);
}
LL slope_down(LL j,LL k)
{
	return 2*a[j]-2*a[k];
}
int main()
{
	while(~scanf("%lld %lld",&n,&m))
	{
		for(LL i=1;i<=n;i++)
			a[i]=a[i-1]+read();
		LL head=1,tail=0;
		q[++tail]=0;
		dp[0]=0;
		for(LL i=1;i<=n;i++)
		{
			while(head<tail && slope_up(q[head+1],q[head])<=a[i]*slope_down(q[head+1],q[head])) head++;
			//维护队首,如上述,当小于时就一定不比q[head+1]优。避免精度问题,使用乘法。 
			dp[i]=dp[q[head]]+(a[i]-a[q[head]])*(a[i]-a[q[head]])+m;
			while(head<tail && slope_up(q[tail],q[tail-1])*slope_down(i,q[tail])>=slope_up(i,q[tail])*slope_down(q[tail],q[tail-1])) tail--;
			//同样维护凸包,使用乘法。 
			q[++tail]=i;
		}
		printf("%lld\n",dp[n]);
	}
}

注:本篇题解只是斜率优化入门,没有深究,后面做到难题时再补几发博客吧

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值