模型化理解单调队列优化和斜率优化DP

6 篇文章 1 订阅
2 篇文章 0 订阅
设A(x),B(x),C(x),D(x)为仅关于x的一元函数


单调队列DP


DP转移方程需要满足的条件:


dp[i]=A(i)+B(j)中的最小/大值 (i-k<=j<i,k为常数)


例子:


dp[i]=dp[j]+(i-j)*w // 选自hdu3401
A(i)=i*w
B(j)=dp[j]-j*w


分析:


维护B(j)的最大合法值进行转移即可


实现:


建立一层循环i=1~n
每次先用i-k更新单调队列队头位置,然后用单调队列中的最值点更新dp[i],最后将B(i)加入单调队列
struct dddl//单调队列 
{
	int q[maxn];
	int p[maxn];
	int h,t;
	void clear(){h=1,t=0;}
	void eliminate(int minp){while(p[h]<minp&&h<=t) h++;}
	void insert(int pos,int val)
	{
		while(q[t]<=val&&h<=t) t--;
		q[++t]=val;
		p[t]=pos;
	}
	int top(){return q[h];} 
}que;
int dp[maxn];
void dynamic_programming()//以取最大值为例
{
	que.clear();
	dp[0]=0;
	que.insert(0,0);//预处理,具体函数值视情况而定 
	for(int i=1;i<=n;i++)
	{
		que.eliminate(i-k);
		dp[i]=que.top()+A(i);
		que.insert(i,B(i));
	}
} 

斜率优化DP


DP转移方程需要满足的条件:


dp[i]=-A(i)*B(j)+C(i)+D(j) 中的最小值,j<i,A(x),B(x)为增函数
dp[i]=-A(i)*B(j)+C(i)+D(j) 中的最大值,j<i,A(x)为减函数,B(x)为增函数


例子:


dp[i]=dp[j]+(s[i]-s[j])^2 /*此处指平方*/ +M // 选自HDU3507
A(i)=s[i]
B(j)=s[j]*2
C(i)=s[i]*s[i]+M
D(j)=s[j]*s[j]+dp[j]


分析:


以取最小值为例
当从j>k,j转移到i比从k转移到i更优时有
-A(i)*B(j)+C(i)+D(j)<-A(i)*B(k)+C(i)+D(k)
变形得[D(j)-D(k)]/[B(j)-B(k)]<A(i)
设g(j,k)=[D(j)-D(k)]/[B(j)-B(k)]
可以证明,对于a<b<c,若g(c,b)<g(b,a),b永远不可能作为转移的最优决策,因为当g(c,b)<A(i),c比b优,当g(b,a)>g(c,b)>A(i),a比b优,那么b点可以删除
把D(j)当作y,B(j)当作x,那么g(j,k)就是连接j,k两点的线段斜率,最后维护出的队列为一个下凸包,如下图
当转移到i时我们选择前面的斜率小于A(i)后面斜率大于A(i)的点转移,顺便把队头定义到这个位置。
设找到点j,这样可以保证对于任意k>j有g(k,j)>A(i),j更优;对于任意k<j有g(j,k)<A(i),j还是更优


实现:


我们需要维护一个队列,使得其中元素两两之间的斜率递增
因为A(i)单增,所以可以定期删除队头元素,每次转移从队头取然后将新的点加入队尾并维护斜率单增
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
int n,m;
long long dp[maxn];
int que[maxn];
long long ky(int a,int b)
{
	return D(a)-D(b);
}
long long kx(int a,int b)
{
	return B(a)-B(b); 
}
void dynamic_programming()//以取最小值为例 
{
	dp[0]=0;//预处理,具体函数值视情况而定 
	que[++t]=0;
	for(int i=1;i<=n;i++)
	{
		while(t>h&&ky(que[h+1],que[h])<=kx(que[h+1],que[h])*A[i]) h++;
		//一定要写成相乘形式,否则会引起除以0,精度低等一系列问题 
		dp[i]=A(i)*B(que[h])+C(i)+D(que[h]);
		while(t>h&&ky(i,que[t])*kx(que[t],que[t-1])<=ky(que[t],que[t-1])*kx(i,que[t])) t--;
	 	que[++t]=i;
	}
 } 





  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值