• $CSP2019$算法总结---$DP$


    #区间$DP$ 题目清单: ![](https://img2018.cnblogs.com/blog/1564177/201910/1564177-20191022163131634-367793133.png) #状态设计 $1.$对于这一类问题,一般设计状态为$dp[i][j]$,表示完成$i-j$区间的答案。 $2.$有一些奇葩的情况,可以这样设计状态:$dp[i][j]$表示完成$j$时,左端点为$i$,右端点为$j-1$ #状态转移 思想是用已经更新的小区间扩展到未被更新的大区间,关键在于如何更新,如何扩展 一种办法是枚举断点把一个大区间划分成小区间 另一种办法是奇葩的倍增思想[传送门](https://i.cnblogs.com/EditPosts.aspx?postid=11720569&update=1)

    有三种(DP)顺序
    (1.)先枚举区间长度,再枚举左端点,再枚举断点,因为区间长度从小到大,所以子状态一定都被更新,切忌先枚举左端点。
    (2.)倒序枚举,即左端点((i))(n->1),右端点((j))(i->n),断点(i-->j)
    (3.)记忆化搜索,这样做无需考虑枚举顺序
    (4.)倍增思想,(dp[i-1][dp[i-1][j]]),见上面链接

    需要注意几点:
    (1.)注意初始化区间长度为(1)时的(DP)值,求最小值还需要初始化所有区间为最大值
    (2.)对于环应该先将它断开变成两倍长的链再区间(DP),最后对于所有(dp[i][i+n])取答案
    (3.)有时候最优答案不一定会出现在(dp[1][n]),需要对于(DP)过程中每一个值取最优值。

    (DP)优化

    一、四边形不等式
    使用范围:区间序列(DP)求最小值(一定是最小值)
    对于动态规划转移方程

    dp[i][j]=min(dp[i][k],dp[k+1][j])+w(i,j);
    

    其中(w(i,j))只受(i,j)取值影响
    如果满足下面两个条件
    (1.)区间单调性:如果对于(forall i leq i'< j' leq j,w(i',j') leq w(i,j))(即小区间取值(leq)大区间取值)
    (2.)四边形不等式:(forall i leq i'< j' leq j,w(i,j)+w(i',j')leq w(i',j)+w(i,j'))

    即中的红线总长(geq) 蓝线总长

    如果(w(i,j))同时满足区间单调性和四边形不等式,那么(f(i,j))满足四边形不等式

    (S(i,j))(F(i,j))在取到最优解时的决策点(k)

    那么决策本身具有单调性,即满足(S(i,j)leq S(i,j+1)leq S(i+1,j+1))

    (j)代替(j+1)得到

    (S(i,j-1)leq S(i,j)leq S(i,j+1))

    转移方程变为

    (F(i,j)=min(F(i,k)+F(k+1,j))+w(i,j); (S(i,j-1)leq kleq S(i+1,j)))

    可以证明,他将时间复杂度降到了(O(n^2))

    什么时候使用四边形不等式?

    只需要牢记公式

    (S(i,j-1)leq S(i,j)leq S(i,j+1))

    考试时可以打一张决策表看是否满足上面式子,满足可以使用四边形不等式

    (1.)序列(DP)有时可以使用四边形不等式优化,但仅仅是常数优化

    (2.)需要注意四边形不等式仅针对求最小值的情况

    (3.)注意(S)数组(下标取值范围)需要初始化,(S[i][i]=i)

    树形(DP)

    题目清单:

    状态设计

    对于这一类(DP),一般设计两维状态,(DP[i][j])表示当前节点为(i),子节点或相邻节点的状态为(j)时的答案

    状态转移

    (1.)树形结构天然的(dfs)序保证了更新当前节点时子节点已经被更新完毕,所以就把(DP)过程放到(dfs)遍历中即可,注意初始化。
    一般每搜索一颗子树回溯回来就更新答案,最后在循环外面向上回溯前自己更新自己答案
    (2.)当然还有种状态转移是需要所有相邻节点的状态,对于这种问题直接(for)循环枚举所有点即可,但要注意枚举顺序,是否会出现状态没有的情况,用多维循环内层枚举当前点,最内层枚举相邻点


    状压(DP)

    题目清单
    MeDSWF.png

    状态设计

    状压(DP)是一类非常特殊的(DP),基本思想是把(DP)的状态压缩为二进制等,以减小空间且保证转移正确性
    一般看到一个题数据范围是(n<=18)且是动态规划,则大概率是状压(DP)
    状态设计时,一维用二进制记录状态,一般在((1<<n)-1)左右,如果需要可以设计另一维表示当前位置,对于很多与选择顺序无关的题目这一维可以省略。
    也有时候第二维可以分割为另一个(DP)数组,大大优化空间

    状态转移

    (1.)位运算的使用:位运算可以提高效率且减少代码复杂度

    s<<=1 //左移*2
    s>>=1 //右移/2 
    (s&(1<<i-1)) //判断第i位是不是1
    s|=(1<<i-1)  //把第i位设置成1
    s&=~(1<<i-1) //把第i位设置成0
    s^=(1<<i-1) //把第i位取反
    s&=s-1     //把s最靠右的1去掉
    s&(-s)     //返回最靠右的1代表的值
    for(s0=s;s0;s0=(s0-1)&s) //依次枚举s的子集
    (1<<n)-1   //n位全是1的状态
    (s&((1<<n)-1)) //只保留s的前n位,避免越界 
    

    (2.)状压(DP)转移
    重点在于考虑记录哪些状态

    一般一维枚举前一个状态(一般是最外层),另一维枚举转移什么(一般是一个值);少数情况第二维枚举接下来状态,因为这样复杂度极大,一般需要剪枝;极少数情况很毒瘤,表面上看起来只能枚举两种状态,但可以拆到另外一个(DP)数组,变为第一种转移方式,比如装箱问题,要求数量最小,可以用一个(DP)记录最后一个箱子剩余空间,从而(O(Sn))转移,不过要注意辅助(DP)数组被更新的前提是正常(DP)数组能被更新,是一个依赖关系,也就是在保证第一答案最优时第二答案最优,这样是对的
    传送门
    (3.)优化问题
    ((1))很多无用状态可以不枚举,比如炮兵阵地问题,可以预处理每一行的合法情况装进(vector)(DP)时只需要枚举这些情况,大大降低复杂度。
    ((2))还有一类问题:愤怒的小鸟,在所有已有状态中枚举找到一个值,再枚举不在状态中的值,复杂度是(O(n^2)),但最优解与选择顺序无关,所以可以控制每次选择最小位置更新,复杂度(O(n))
    ((3))对于区间、字符串操作,可以压缩状态后连边,跑最短路求解
    ((4))与考虑顺序无关的(DP)可以优化掉一维,只与附近固定位置有关,可以使用滚动数组优化

    一定要小心MLE!!!!!


    数位(DP)

    题目清单(做的比较少)
    MljmOP.md.png

    状态设计

    一般数据范围为(10^{18})
    通常使用多维,每维规模比较小。
    (dp[i][j][k][0/1])表示当前在第(i)位(从最低位到最高位编号),当前位置是(j),附近位置的状态是(k),是否满足题目要求条件时的方案数(一般记忆化搜索(k)表示前面状态,递推(dp k)表示后面状态,记忆化搜索还需要记录是否小于边界)

    状态转移

    数位(DP)一般有两种做法:(1.)递推计数,(2.)记忆化搜索,两者的核心思想:逼近法,逐位确定是一样的

    逼近法(先讨论(1-x)的答案)

    分两种情况讨论,一种是位数(<cnt),一种是和答案位数相同,一般不考虑前导零,第一种情况需要单独处理
    对于第一种情况,位数小于边界位数,枚举的数一定小于边界,枚举位数是多少,第一位(in [1,9]),后面位置随便取数即可,直接计数。
    对于第二种情况,逐位确定,首先最高位(in [1,a[cnt]))时一定是小于边界的,所以后面位置随便取,计数,然后确定最高位为(a[cnt]),处理下一位,最终逼近到边界(需要注意的是最后会停在(x-1),可以单独处理(x),或者提前把边界(+1))。每次都不卡到边界上,保证后面所有位置可以随意取值便于计数
    下面以(65536)为例看一下如何逼近((x)表示该位置可以随意取数)
    M1pXWD.png
    如果题目要求([L,R])的答案,直接求(ans[L-1],ans[R]),作差即可。

    继续回到状态转移上
    对于递推计数的方法,首先要预处理(dp)数组(本身是数位(DP)),再按照逼近法计数,状态多的时候预处理会很麻烦,需要边界(+1)

    对于记忆化搜索,只需要边搜索边记录(DP)数组即可,对于这种方法,状态转移比较简单,码量小,比较容易思考,边界不需要提前(+1)

    注意的问题

    (1.)递推计数预处理时有时可以用前缀和等等方法优化
    (2.)初始值为(-1)时,小心数组下标越界
    (3.)看好题目范围中是否包含(0)
    (4.)前导零情况单独处理

  • 相关阅读:
    Scrapy数据持久化
    Mybatis源码与Spring源码中设计模式的应用总结
    Count-Min Sketch 算法
    加解密算法、消息摘要、消息认证技术、数字签名与公钥证书
    智能卡系统设计(一) 断电保护和数据备份
    TCP/IP协议——ARP详解
    DES算法详解
    Python调用外部程序——os.system()和subprocess.call()
    Python实现截图
    Linux笔记:SSH客户端断开配置
  • 原文地址:https://www.cnblogs.com/Liuz8848/p/11718615.html
Copyright © 2020-2023  润新知