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

设A(x),B(x),C(x),D(x)为仅关于x的一元函数


单调队列DP


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


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


例子:


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]

斜率优化DP


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


dp[i]=-A(i)*B(j)+C(i)+D(j) 中的最小值,j
dp[i]=-A(i)*B(j)+C(i)+D(j) 中的最大值,j


例子:


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)] 设g(j,k)=[D(j)-D(k)]/[B(j)-B(k)]
可以证明,对于ag(c,b)>A(i),a比b优,那么b点可以删除
把D(j)当作y,B(j)当作x,那么g(j,k)就是连接j,k两点的线段斜率,最后维护出的队列为一个下凸包,如下图 模型化理解单调队列优化和斜率优化DP_第1张图片
当转移到i时我们选择前面的斜率小于A(i)后面斜率大于A(i)的点转移,顺便把队头定义到这个位置。
设找到点j,这样可以保证对于任意k>j有g(k,j)>A(i),j更优;对于任意k


实现:


我们需要维护一个队列,使得其中元素两两之间的斜率递增
因为A(i)单增,所以可以定期删除队头元素,每次转移从队头取然后将新的点加入队尾并维护斜率单增
#include
#include
#include
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;
	}
 } 





你可能感兴趣的:(OI,动态规划,HDU)