斜率优化模板题 HDU3507:Print Article

本文介绍了一种在动态规划问题中使用斜率优化的方法,通过维护一个单调队列来实现状态转移,从而将时间复杂度从O(n^2)降低到O(n)。这种方法适用于特定形式的状态转移方程。

首先看出是DP

方程 dp[i]=min{dp[j]+(sum[i]-sum[j])^2+m}.

n的范围是<=500000,二维动归会超时。

那末,下面是斜率优化:

在做状态i的时候,我们会比较在i前面的状态j和状态k

我们假设j>k;

那末,如果状态j更优,就有(dp[j]+(sum[i]-sum[j])^2)>(dp[k]+(sum[i]-sum[k]))

经移项得((dp[j]+sum[j]^2)-(dp[k]+sum[k]^2))/(2*sum[j]^2-2*sum[k])<sum[i]

设dp[j]+sum[j]^2=y1,dp[k]+sum[k]^2=y2,2*sum[j]^2=x1,2*sum[k]^2=x2,把点(x1,y1)和(x2,y2)投射到坐标系中,上面的式子就是过这两点直线的斜率。

设这个斜率为g(j,k)。

弄一发类似单调队列的东东,存位置,维护队列使队列内相邻的点斜率单调上升。

取数时,先取出头指针和下一个位置的斜率,如果斜率比sum[i]小,那末说明这时取后一个数更优,头指针后移。

进队列时,比较尾指针和前一个位置的斜率与需要加入的点和尾指针的斜率,如果前者较大,说明加入了状态i后,不满足斜率递增,那末尾指针前移。

时间复杂度降为O(n)。

代码:

#include<cstdio>
#include<cstring>
#include<algorithm>

using namespace std;
typedef long long ll;
ll dp[500000+10],sum[500000+10],dui[500000+10];
int n,m,i,j,a,tou,wei;

ll up(int x,int y)//y1-y2
{
	int j=x,k=y;	
	return dp[j]+sum[j]*sum[j]-dp[k]-sum[k]*sum[k];
}
ll down(int x,int y)//x1-x2
{
	int j=x,k=y;
	return 2*(sum[j]-sum[k]);
}
int main()
{
	while(scanf("%d%d",&n,&m)==2)
	{
		memset(sum,0,sizeof(sum));
		for(i=1;i<=n;++i)
		{
			scanf("%d",&a);
			sum[i]=sum[i-1]+a;
		}
		memset(dui,0,sizeof(dui));
		memset(dp,0,sizeof(dp));
		tou=0;
		wei=1;
		dui[1]=1;
		dp[1]=sum[1]*sum[1]+m;
		for(i=2;i<=n;++i)
		{
			while(tou<wei&&up(dui[tou+1],dui[tou])<=sum[i]*down(dui[tou+1],dui[tou]))
			++tou;<span style="white-space:pre">	</span>//后面的状态更优,后移前指针
			j=dui[tou];
			dp[i]=dp[j]+m+(sum[i]-sum[j])*(sum[i]-sum[j]);//状态转移
			while(tou<wei&&up(i,dui[wei])*down(dui[wei],dui[wei-1])<=up(dui[wei],dui[wei-1])*down(i,dui[wei]))
			wei--;<span style="white-space:pre">	</span>//不满足斜率递减,前移尾指针
			dui[++wei]=i;//加入状态i
		}
		printf("%lld\n",dp[n]);
	}
	return 0;
}
C++新手,代码又丑又LOW不喜勿喷。。。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值