用途

废话,当然是在DP式子满足某些性质的时候来优化复杂度……

定义

对于\(j\)往大于\(j\)的\(i\)转移,可以表示成一个关于\(i\)的函数\(f_j(i)\),也就是\(dp_i=\max/\min\{f_j(i)\}\)。

若是取\(\max\),并且在某一个地方\(f_j(i)\)从下面跑到了\(f_k(i)\)的上面(如果加入\(f_j(i)\)这个函数时本来就在\(f_k(i)\)的上面那么不算),就称之为\(j\)取代了\(k\)。取\(\min\)同理。

如果满足决策单调性,那么必须满足:对于任意\(i,j\in [1,n]\),\(f_j(x)\)与\(f_i(x)\)没有交点或是只有一个交点,也就是\(i\)和\(j\)最多取代一次。

狭义的决策单调性是指对于\(i<j\)和他们的决策点\(p_i,p_j\),会有\(p_i<p_j\)。也就是说,对于\(j<k\),就只能有\(k\)取代\(j\)。

广义的决策单调性指只要一个题中取代关系唯一(只有大取代小或小取代大),那么都可以做。

显然,狭义决策单调性也被包含在广义决策单调性内。

显然,狭义和广义是我自己编出来的。

狭义决策单调性

分治

简单易懂,适合\(dp_i=\max/\min\{g_j+w_{i,j}\}\)这种与\(dp_j\)无关的递推式。

考虑一个类似整体二分的做法:设\(solve(l,r,L,R)\)表示已知\([l,r]\)全部由\([L,R]\)转移而来,求出\(dp[l,r]\)。

每次设\(mid=(l+r)/2\),然后暴力扫\([L,R]\),找到\(mid\)的决策点\(p\),然后分治\(solve(l,mid-1,L,p)\),\(solve(mid+1,r,p,R)\)。

复杂度:会做\(\log n\)层,每层的\(\sum(r-l+R-L)\)都是\(O(n)\)的,所以总复杂度\(n\log n\)。

优点:当\(w_{i,j}\)不能\(O(1)\)或\(O(\log n)\)计算时用暴力扫的方式或许可以做出来。

缺点:转移不按顺序,所以递推式必须与\(dp_j\)无关。

注意:如果做的时候还扫了\([R,l]\),那么复杂度分析就会挂!

单调队列

适合\(dp_i=\max/\min\{dp_j+w_{i,j}\}\)的递推式,要求\(w_{i,j}\)可以快速求出。

狭义决策单调性保证了比较靠前的决策点被取代后就可以被忽略了,所以可以维护一个队列,队首是当前最优的决策点,队尾是后面加入、还有潜力取代前面的决策点。(记\(q[l]\)为队首,\(q[r]\)为队尾)

每次考虑\(q[l]\)和\(q[l+1]\),如果\(q[l]\)没那么优,那么就把它弹掉。

加入当前点的时候,就要搞一些事情了。

如果什么都不管直接加入,那么会出现这样的尴尬情况:\(q[l]\)优于\(q[l+1]\),但\(q[l]\)劣于\(q[l+2]\)。

如何避免呢?使用上面\(f_j(x)\)函数数形结合的思想,如果加入这个点\(i\)后\(q[r]\)潜力就没了,也就是\(i\)取代\(q[r-1]\)比\(q[r]\)取代\(q[r-1]\)还快,那么就把\(q[r]\)弹掉。这样一来,上面的\(q[l+1]\)根本就不会出现在队列里面。

至于判断在哪里会取代,那就要二分那个位置了。

复杂度:显然\(O(n\log n)\)。

优点:转移按顺序。

缺点:要求\(w_{i,j}\)可以快速求出。

广义决策单调性

大取代小的时候上面已经讲过,不再赘述。

单调栈

现在是小取代大,比如\(f_2(i)\)一直默默无闻,而\(f_4(i)\)一直是统治地位,突然在\(i=10\)的时候2把4取代了。

这种时候,就不能再用单调队列,而是要用单调栈了。当栈顶没有下面的函数优的时候就弹栈。压入当前函数时若\(st[top-1]\)取代\(i\)比\(st[top]\)还快就弹栈。

本质上其实和单调队列差别不大,也是要二分,也是要求\(w_{i,j}\)能快速求出,复杂度也是\(O(n\log n)\)。

不过小取代大这种情况只有这一种做法,所以就没有优缺点一说了。

判断决策单调性

讲了这么多做法,还没有讲怎么判断这一性质。

一般来说应该是先写出DP式子,各种优化方法试一遍都不行,那就尝试严谨或感性证明。

如果实在不会证,那可能可以打表猜结论。猜中了当然好,但如果随机数据都过了,而结论其实是假的,那……

那你就要祈祷出题人用的就是随机数据。

一般来说我倾向于随机数据猜结论。

最新文章

  1. Mono for android 如何动态添加View,线程内部如何更新UI.
  2. POJ - 1107 W&#39;s Cipher
  3. 百分比定位加position定位的常用布局
  4. 工厂方法模式(FACTORY METHOD)
  5. 配置nginx下别名alias支持PHP fastcgi解析
  6. onInterceptTouchEvent和onTouchEvent举例分析
  7. 将list&lt;对象&gt;转换成DataTable,把DataTable转换成参数传入存储过程实现批量插入数据
  8. Linux随笔
  9. hdu 1848 Fibonacci again and again(简单sg)
  10. 快速破解ps方法
  11. struts2-----新建项目
  12. centos 安装gcc-&gt;联网 问题解决
  13. MySQL Index Merge Optimization
  14. Python模拟登录成功与失败处理方式(不涉及前端)
  15. 谁说深入浅出虚拟机难?现在我让他通俗易懂(JVM)
  16. javascript中正则表达式和ruby中的一点差异
  17. Jenkins+Git+Maven搭建自动化构建平台
  18. JAVA程序设计的第一次作业
  19. Atom使用
  20. HNUOJ 13341

热门文章

  1. ELK搜索条件
  2. .net core使用CSRedisCore连接哨兵集群,并用作redis使用分布式缓存。
  3. 基于【 建造者模式】一 || 网关zuul过滤器封装
  4. hystrix配置
  5. npm查看包版本
  6. 使用httpwebrequest发送http请求
  7. Cookie实现记住密码的功能
  8. redo log和bin log
  9. 每日一题-——LeetCode(486) 预测赢家
  10. python-----opencv图像边界扩充