决策单调性学习笔记

决策单调性

决策单调性是一种优化动态规划的方法,其思想就是对于满足决策点的位置存在单调性的转移方程,根据这一个性质对其进行优化。

首先设 DP 的代价函数为 w(l,r) 表示 lr 之间的贡献。

四边形不等式

  • 若对于满足 ii+1jj+1 的任意 i,j,都有(若要求最大值,则符号相反):

w(i,j)+w(i+1,j+1)w(i+1,j)+w(i,j+1)

包含单调性

  • 对于任意满足 llrrl,l,r,r ,都有:

w(l,r)w(l,r)

如果一个 DP 中代价函数满足四边形不等式和包含单调性,则转移方程满足决策单调性。

1D 的证明

假设存在 x,y 满足 xy ,设 pifi 的转移点,假设 x,y 满足 pxpy

由于 DP 求的是最小值,那么:

fx=fpx+w(px,x)fpy+w(py,x)

由于代价函数满足四边形不等式,并且此时有 pxpyyx,那么:

w(px,y)+w(py,x)w(px,x)+w(py,y)

两式相加得:

fpx+w(px,y)fpy+w(py,y)=fy

此时可以发现,用 px 来转移 y 比用 py 更优,与假设相矛盾,因此命题成立。

具体做法

CF321E Ciel and Gondolas

简要题面:

总共有 n 个人站成一排,编号 1n ,共有 k 条船,每条船可以运走队首的任意个人,要求每条船都要坐人且 k 条船要将 n 个人全部运走。每个人和其他的人都会有一个沮丧值 ui,j 表示如果 i,j 在同一条船上就会产生 ui,j 的沮丧值,求最小沮丧值。

分治做法

Solve(l,r,L,R) 表示 flfl+1、.......fr 的决策点在 [L,R] 中。

我们暴力枚举所有的转移点用来更新 fmid=(l+r)/2 ,记录 fmid 的最优转移点记为 p

由于满足决策单调性,因此 flfmid1 的最优转移点在 [L,p] 中,fmid+1fr 的最优转移点在 [p,R] 之中,对于两边递归处理即可。

递归的层数显然最多为 logn ,每层内处理的时间复杂度是 o(n) 级别的,因此总复杂度是 o(nlogn)


int mid,up,pos;
inline void Solve(int l,int r,int L,int R) {
	if(l>r) return ;
	if(L==R) {
		for(int i=l;i<=r;i++) f[i]=Calc(L,i);
		return ;
	}
	mid=l+r>>1; up=min(mid-1,R);
	f[mid]=Calc(L,mid); pos=L; 
	for(int i=L+1,val;i<=up;i++) 
		if((val=Calc(i,mid))<f[mid]) f[mid]=val, pos=i;
	Solve(l,mid-1,L,pos); Solve(mid+1,r,pos,R);
} 

二分栈做法

维护一个形如 f=0,0,1,1,1,2,3,3,4,6 的序列。

考虑这个序列的具体含义:

F[1]、F[2]     	由 F[0] 转移而来
F[3]、F[4]、F[5]  由 F[1] 转移而来
F[6] 			 由 F[2] 转移而来
F[7]、F[8]		由 F[3] 转移而来
.......

具体实现我们维护一个单调栈。对于栈中的每一个二元组 x,p 表示 F[x] 可用来更新 F[p]F[n] 栈中满足 p 单调递增。

  • 更新答案

那么假设此时要更新 F[i] ,我们只需要找到在栈中的第一个满足 p>i 的前一个位置,用这个位置记录的 x 就是 F[i] 的最优转移点。

  • 更新单调栈

依次检查栈顶的元素 val,如果用 F[i] 来更新 F[val.p] 比使用 F[val.x] 更优,那么 val 就失去了存在的价值,我们把它弹出栈。

弹到不能再弹为止,对于栈顶的元素 val ,我们二分出第一个满足用 F[i] 更新比用 F[val.x] 更新更优的位置,记为 pos ,我们将 i,pos 这个二元组加入单调栈。


inline int Get(int x,int y,int l) {
	int r=n;
	while(l<=r)
		if(Calc(y,mid)>Calc(x,mid)) l=mid+1;
		else r=mid-1;
	return l;
}

inline void DP(int id) {
	sta[topf=bt=1]=data{id-1,id};
	for(int i=id,p;i<=n;i++) {
		p=topf;
		while(p>bt && sta[p].p>i) --p;
		bt=p;
		f[i]=Calc(sta[p].x,i);
		while(topf>=bt && P>i && Calc(X,P)>=Calc(i,P)) --topf;
		if(topf<bt) sta[++topf]=data{i,i+1};
		else { now=data{i,Get(X,i,max(P+1,i+1))}; if(now.p<=n) sta[++topf]=now; }
	}
}

2D 的转移

p[l][r]f[l][r] 的决策点。

由于满足决策单调性,因此:

p[l][r1]p[l][r]p[l+1][r]

直接根据这个限制枚举范围即可。

f[0][0]=0;
    for(int i=1;i<=k;i++) {
        p[n+1][i]=n;
        for(int j=n;j>=1;j--)
            for(int k=p[j][i-1];k<=p[j+1][i];k++) {
                int w=calc(k,j);
                if(f[k][i-1]+w<f[j][i]) f[j][i]=f[k][i-1]+w, p[j][i]=k; 
            }
    }

待更。

posted @   _YangZJ  阅读(42)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!
点击右上角即可分享
微信分享提示
主题色彩