HDU 3507 Print Article DP(斜率优化入门)

本文介绍了一种解决特定动态规划问题的高效算法——最小代价分组问题,该问题涉及寻找一组数字连续写在一起的最小代价,通过斜率优化实现了时间复杂度从O(n^2)降低至O(n),并提供了完整的实现代码。

点击打开链接

参考链接

题意:n个数有代价ci n<=5e5,若将k个数连续写在一起的代价为:(sigma(c[i]))^2+M i=x+1~x+k,M为常数 求写出n个数的最小代价
s[i]为i的前缀和,设d[i] 写出前i个数的最小代价 d[i]=min(d[j]+(sum[i]-sum[j])^2+M) j=1~i j+1~i为最后一段长度
n<=5e5,直接做O(n^2)显然TLE,注意方程中sum[i]是递增的,利用斜率优化 时间复杂度降为O(n)


结论1:

考虑决策j,k 若决策j优于决策k d[j]+(sum[i]-sum[j])^2+M<d[k]+(sum[i]-sum[k])^2+M
化解得 (d[j]+sum[j]^2)-(d[k]+sum[k]^2)/(2*(sum[j]-sum[k])) < sum[i] 令yi=d[i]-sum[i]^2,xi=2*sum[i]
则:(yj-yk)/(xj-xk)<sum[i] 得决策j优于k当且仅当斜率g(j,k)<sum[i]


结论2:

k<j<i 若g(i,j)<g(j,k) 则j永远都不可能为最优决策 可以永久剔除 
情况1:g(i,j)<sum[x] i优于j
情况2:g(i,j)>sum[x] j优于i 但是g(j,k)>g(i,j)>sum[x] k优于j.


则只剩下点满足g(i,j)>g(j,k) 从左到右斜率是递增的
可以用单调队列来维护可能的最优决策,队列元素a,b,c 
新的点为d,若g(d,c)<g(c,b) 由上可知,队尾c出队 直到g(d,c')>=g(c',b') d加入末尾 ,队列中相邻两点的斜率是递增的. 


对于某个状态i,若g(head+1,head)<sum[i] 则head+1优于head 又因为sum[i]是递增的 所以head可以出队(后面肯定不会用到)
直到g(head+1,head)>=sum[i] head优于head+1,由于单调队列递增 后面g(head+2,head+1)>=sum head+k优于head+k+1 k=0,1,2...
此时head为最优决策 执行DP(i,q[head])即可

  

#include <bits/stdc++.h>
using namespace std;
const int N=5e5+20; 
int n,M,c[N],s[N]; 
int dp[N];
int head,tail,q[N];
int DP(int i,int j)
{
	return dp[j]+(s[i]-s[j])*(s[i]-s[j])+M;
}
int dy(int j,int k)//yj-yk
{
	return (dp[j]+s[j]*s[j])-(dp[k]+s[k]*s[k]);
}
int dx(int j,int k)//xj-xk
{
	return 2*(s[j]-s[k]);
}
int main()
{
	while(cin>>n>>M)
	{
		s[0]=dp[0]=0;	
		for(int i=1;i<=n;i++)
			scanf("%d",&c[i]),s[i]=s[i-1]+c[i];
		head=tail=0;
		q[tail++]=0;//决策:取一整段
		//每个点最多进出队一次O(N) 
		for(int i=1;i<=n;i++)
		{
			//找到最优决策head,根据sum[i]递增&&g(h+1,h)<=sum[i] 淘汰head
			while(head+1<tail&&dy(q[head+1],q[head])<=s[i]*dx(q[head+1],q[head]))
				head++;
			dp[i]=DP(i,q[head]);	
			
			//g(i,j)<g(j,k) 维护最优决策集合 
			while(head+1<tail && dy(i,q[tail-1])*dx(q[tail-1],q[tail-2])<=dy(q[tail-1],q[tail-2])*dx(i,q[tail-1]))
				tail--;
			q[tail++]=i;
		}
		cout<<dp[n]<<endl; 
	}
	return 0;
}






评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值