单调性优化dp主要有2种:
1.dp本身的单调性。dp有多种取值,取某一部分有一个分界线。
使用这个性质可以快速计算dp值
这种dp的性质一般只能靠猜。也能通过思考,打表得来。
例题有:
IOI2018 会议
CF573E
NOIP2019 划分
2.决策单调性。
决策单调性通常用于1d-1d dp。它的转移方程通常是:$f[i]=min(f[j]+w(i+1,j))$或$f[i]=max(f[j]+w(i+1,j))$
对于$a<b<c<d$,当b转移到c比a转移到c优,则b转移到d比a转移到d优
从这个可以推出一个结论:不可能出现i<j,且i的决策点位置>j的决策点,实际上就是决策点是有序的
实际上就是如下2个公式:
$f[b]+w(b+1,c)<f[a]+w(a+1,c)$
$f[b]+w(b+1,d)<f[a]+w(a+1,d)$
联立得到
$w(b+1,c)-w(b+1,d)<w(a+1,c)-w(a+1,d)$
$w(b+1,c)+w(a+1,d)<w(a+1,c)+w(b+1,d)$
实际上会发现,当$mathrm{w} (i, j + 1) + mathrm{w} (i + 1, j)leqmathrm{w} (i, j) + mathrm{w} (i + 1, j + 1)$时上面的公式是成立的。
如果设$f(i)=w(i,j)-w(i,j+1)$,则上面的式子变成了$f(i)>=f(i+1)$,就是当i<j时,$f(i)>=f(j)$
所以可以得到$f(b+1)<=f(a+1)$,$w(b+1,j)-w(b+1,j+1)<=w(a+1,j)-w(a+1,j+1)$
$w(b+1,j)-w(a+1,j)<=w(b+1,j+1)-w(a+1,j+1)$
如果设$g(j)=w(b+1,j)-w(a+1,j)$,容易得到$g(i)<=g(i+1)$,就是当i<j时,$g(i)<=g(j)$
则$g(c)<=g(d)$,$w(b+1,c)-w(a+1,c)<=w(b+1,d)-w(a+1,d)$
$w(b+1,c)+w(a+1,d)<=w(a+1,c)+w(b+1,d)$
证明了上面的命题。
决策单调性大多数只能靠猜,打表得到。
维护有2种方法:分治法,二分队列法。
分治法的核心代码是:(从f转移到g)
void fz(int l,int r,int x,int y){ if(l>r||x>y)return; int o,md=(l+r)/2; for(int i=x;i<=min(md,y);i++) if(f[i]+val(i+1,md)<g[md]){ g[md]=f[i]+qu(i+1,md); o=i; } fz(l,md-1,x,o); fz(md+1,r,o,y); }
它的意义是:
现在的决策点被锁定在区间[x,y],现在要确定md的决策点,则扫一遍[x,y]
则由于决策点的有序性,[l,md-1]的决策点被锁定在[x,o]内,[md+1,r]被锁定在[o,r]内,继续分治下去可以得到f数组,一次时间复杂度nlogn
优点:代价函数的移动次数是nlogn的。这样在一些题目内可以保证复杂度,且代码容易写。
缺点:必须保证更新g的数组f没有依赖。
二分队列法的核心代码是:
q[1]=(no){1,n,0}; for(int i=1;i<=n;i++){ f[i]=cl(i,q[h].s); la[i]=q[h].s; if(q[h].l==q[h].r)h++; else q[h].l++; while(h<=t&&cl(q[t].l,q[t].s)>cl(q[t].l,i))t--; if(h>t)q[++t]=(no){i+1,n,i}; else{ int l=q[t].l+1,r=q[t].r+1; while(l<r){ int md=(l+r)/2; if(cl(md,q[t].s)>cl(md,i))r=md; else l=md+1; } q[t].r=l-1; if(l<=n)q[++t]=(no){l,n,i}; } }
其中队列的结构体是存储连续的相同的决策点。
每次插入一个点并且更新队列,在队列中二分出被更新的决策点。
优点:dp数组之间可以有依赖
缺点:必须保证一次计算代价函数的时间复杂度较小,否则复杂度会退化。且代码比分治法更难写。
例题:
uoj285(没做)
诗人小G
CF868F
除此之外,带四边形不等式的dp也可以带权二分,详见https://www.cnblogs.com/Itst/p/12805678.html