zoukankan      html  css  js  c++  java
  • DP(我的比较菜,很垃圾)

    动态规划基础
    清华大学 赵和旭



    前言
    1:我们先不对动态规划做一些枯燥的定义。
    2:而是看两个简单的问题,看看如何分析和解决这样的问题,从这几道
    题的分析过程共同点或者说特点中,归纳总结出动态规划的一般特点和
    思路。
    3:然后再通过几道例题来强化理解我们总结的动态规划做题思路。
    4:之后我们将从另一角度理解动态规划,即记忆化搜索视角。
    5:我们还将理解DP一种简单情况的转移路径, DAG上的DP
    6:再通过大家独立思考,实践两道题目,实现对dp算法的初步运用。
    2019/7/17 Wednesday 清华大学——赵和旭 2
    前言
    7:一般形式的背包dp
    8:一般形式序列上的dp
    9:一般形式的区间dp
    2019/7/17 Wednesday 清华大学——赵和旭 3
    填满网格
    给出一个2*n的网格,你现在需要用n2*1的多米诺骨牌占满整个棋盘。
    多米诺骨牌可以横着或者竖着放。
    求有多少方案数占满整个棋盘。
    N<=10^6
    2019/7/17 Wednesday 清华大学——赵和旭 4
    填满网格
    f[n]n列的答案
    则根据如何对最后一两列放多米诺分情况讨论可得
    f[n]=f[n-1]+f[n-2]
    这是递推,也算是一个简单的dp,只不过绝大多数dp的转移方程不是根
    据这题一样简简单单的一个加号就解决的。
    2019/7/17 Wednesday 清华大学——赵和旭 5
    网格图路径计数
    给出一个n*m的网格,每次只能向右或者向下走,求从(1,1)走到(n,m)的方
    案数,其中有些位置是不能走的。
    n,m<=1000
    2019/7/17 Wednesday 清华大学——赵和旭 6
    网格图路径计数
    如果没有障碍物:设dp[i][j]表示从(1,1),走到(i,j)的方案数。
    考虑上一步从哪里走过来可得, dp[i][j]=dp[i-1][j]+dp[i][j-1]
    答案就是dp[n][m]
    对于障碍:如果(i,j)是一个障碍,则定义dp[i][j]=0
    2019/7/17 Wednesday 清华大学——赵和旭 7
    走金字塔的最大价值路径
    给出一个高度为n的金字塔,你现在要从金字塔的顶端走到金字塔的底端。
    金字塔的第i层,第j房间(记为(i,j) )有价值为a[i][j]的宝藏,每一步能从(i,j)
    走到, (i+1,j) , (i+1,j+1)。求从金字塔顶部走到底部能获得的最大的价值是多少?
    n=4的一个例子。
    7
    3 8
    8 1 0
    2 7 4 4
    ans=7+3+8+7=25
    2019/7/17 Wednesday 清华大学——赵和旭 8
    走金字塔的最大价值路径
    和前面两题差不多。只不过前面是求方案数,运算法则为加法,而这里
    求最优值,运算法则是取max
    dp[i][j]表示从塔顶走到(i,j)能得到的最大的价值是多少。
    dp[i][j]=max(dp[i-1][j-1],dp[i-1][j])+a[i][j]
    2019/7/17 Wednesday 清华大学——赵和旭 9
    动态规划的基本思想? ——最优化。
    利用最优化原理把多阶段过程转化为一系列单阶段问题,利用各阶段之
    间的关系,逐个求解。
    更具体的,假设我们可以计算出小问题的最优解,那么我们凭借此可以
    推出大问题的最优解,进而我们又可以推出更大问题的最优解。(要满
    足最优子结构)
    (从小问题答案推到大问题的答案)
    而最小的问题也就是边界情况我们可以直接计算出答案来。
    2019/7/17 Wednesday 清华大学——赵和旭 10
    动态规划的基本思想? ——最优化。
    基本思想是将待求解的问题分解为若干个子问题(阶段),按顺序求解
    子阶段,小的子问题的解,为更大子问题的求解提供了有用的信息。
    由于动态规划解决的问题多数有重叠子问题这个特点,为减少重复计算,
    对每一个子问题只解一次。
    就像刚刚那几个递推式我们如果递归来做就会有很多冗余的计算。
    2019/7/17 Wednesday 清华大学——赵和旭 11
    动态规划的状态
    动态规划过程中,需要有状态表示和最优化值(方案值)
    状态表示是对当前子问题的解的局面集合的一种(充分的)描述。
    最优化值(方案值)则是对应的状态集合下的最优化信息(方案值),
    我们最终能通过其直接或间接得到答案。
    2019/7/17 Wednesday 清华大学——赵和旭 12
    状态表示
    对于状态的表示,要满足三条性质
    1:具有最优化子结构:即问题的最优解能有效地从问题的子问题的最优
    解构造而来
    2:具有简洁性:尽可能的简化状态的表示,获得更优的时间复杂度。
    3:同时能够全面的描述一个局面。一个局面有一个答案,而这个局面是
    需要一些参数来描述的。
    设计状态的关键就是 充分描述,尽量简洁。
    2019/7/17 Wednesday 清华大学——赵和旭 13
    动态规划的精髓? ——状态转移
    由于具有最优化子结构(在最优化问题种),所以求当前状态的最优值
    可以通过其他的(较小的问题)状态的最优值加以变化而求出。所以,
    当一个状态的所有子结构都已经被计算之后,我们就可以通过一个过程
    计算出他的最优值。这个过程就是状态的转移。
    注意:状态的转移需要满足要考虑到所有可能性。
    2019/7/17 Wednesday 清华大学——赵和旭 14
    怎么计算动态规划的时间复杂度?
    一般简单动态规划时间复杂度==状态数×状态转移复杂度。
    同时也就引出了dp的两种优化时间的方法
    1:减少状态的维数
    2:加速状态转移,例如数据结构优化或者分析性质
    2019/7/17 Wednesday 清华大学——赵和旭 15
    接下来我将通过一道例题,来对上面所说的思想和精髓包括复杂度的计
    算来进行解释。
    2019/7/17 Wednesday 清华大学——赵和旭 16
    最长上升子序列
    给出一个长度为n的数列 a[1..n], 求这个数列的最长上升子序列。
    就是给你一个序列,请你在其中求出一段不断严格上升的子序列,子序列是不
    一定要求连续的。
    2,3,4,72,3,4,6是序列2 5 3 4 1 7 6的两种选取方案。最长上升子序列的长度就
    4
    N<=1000
    2019/7/17 Wednesday 清华大学——赵和旭 17
    最长上升子序列
    这是一道最为经典的完全用动态规划来解决的问题。
    dp[i]为以a[i]为末尾的最长上升子序列的长度。
    最后的答案就是我枚举一下最长上升子序列的结束位置,然后取一个最
    大值即可。
    问题是如何求这个数组?
    那我们回过头来想一想最开始说的动态规划的基本思想,从小的问题推
    出大的问题。
    2019/7/17 Wednesday 清华大学——赵和旭 18
    最长上升子序列
    假设我们知道了dp[1..(i-1)],我们怎么才能根据这些信息推出来dp[i]
    再次强化一下定义: dp[i]表示以i结尾的最长上升子序列。
    我们只需要枚举这个上升子序列倒数第二个数是什么就好了。
    状态转移: dp[i]=max{ dp[j] | a[j]<a[i] && j<i } +1 ;
    2019/7/17 Wednesday 清华大学——赵和旭 19
    最长上升子序列
    之前的例子:
    A{2 , 5 , 3 , 4 , 1 , 7 , 6}
    dp[1]=1;
    dp[2]=max{dp[1]}+1;
    dp[3]=max{dp[1]}+1;
    dp[4]=max{dp[1],dp[3]}+1;
    时间复杂度 On^2)。
    2019/7/17 Wednesday 清华大学——赵和旭 20
    最长上升子序列
    在这个问题的分析中,突破口?
    1:设计出了dp[1..n]这个可以储存以i结尾的子序列中最优的答案,这个
    状态表示。
    2:通过分析问题成功能将当前状态的最优值能过由之前状态转移出来。
    dp[i]=max{ dp[j] | a[j]<a[i] && j<i } +1 ,状态转移。
    2019/7/17 Wednesday 清华大学——赵和旭 21
    时间复杂度
    套上之前的公式:状态数×状态转移复杂度。
    状态数: dp[1..n],只有n个状态。
    状态转移: dp[i]=max{ dp[j] | a[j]<a[i] && j<i } +1 ;
    由于你每次都需要枚举之前的所有数,所以单次转移是On)。
    所以总复杂度 On^2)。
    2019/7/17 Wednesday 清华大学——赵和旭 22
    最长上升子序列核心代码
    dp[i]为以a[i]为末尾的最长上升子序列的长度。
    状态转移: dp[i]=max{ dp[j] | a[j]<a[i] && j<i } +1 ;
    2019/7/17 Wednesday 清华大学——赵和旭 23
    P1091合唱队形
    N 位同学站成一排,音乐老师要请其中的( N-K )位同学出列,使得剩下的
    K 位同学排成合唱队形。
    合唱队形是指这样的一种队形:设K位同学从左到右依次编号为 1,2,…
    他们的身高分别为 T_1,T_2,…,T_K
    则他们的身高满足 T1<T2<T3..<Ti>T_i+1> ...>Tk
    你的任务是,已知所有N位同学的身高,计算最少需要几位同学出列,可
    以使得剩下的同学排成合唱队形。
    n<=100000
    P1091合唱队形
    我们设f[i]表示以i结尾的最长上升子序列长度。
    我们设g[i]表示以i开头的最长下降子序列长度。
    然后我们枚举哪一个为中心的最高点, f[i]+g[i]-1取最大值即可。
    乘积最大
    设有一个长度为N的数字串,要求选手使用K个乘号将它分成K+1个部分,
    找出一种分法,使得这K+1个部分的乘积能够为最大。
    有一个数字串: 312,当N=3K=1时会有以下两种分法:
    1) 3*12=36
    2) 31*2=62
    这时,符合题目要求的结果是: 31*2=62
    现在,请你帮助你的好朋友XZ设计一个程序,求得正确的答案。
    NK6≤N≤801≤K≤50
    2019/7/17 Wednesday 清华大学——赵和旭 26
    乘积最大
    f[i][a] 表示前 i 位数包含 a 个乘号所能达到的最大乘积,我们只需要枚
    举上一个乘号所在的位置即可。
    j a i - 1      进行一次枚举,表示前 j 位中含有 a-1 个乘号,且最后一个乘号的位置在
    j 处。那么当最后一个乘号在 j 处时最大值为前 j 位中含有
    a - 1 个乘号的最大值乘上 j 处之后到i的数字。
    因此得出了状态转移方程 f[i][a] = max(f[i][a] , f[j][a-1] * cut(j + 1,i))——
    cut(b + 1,i) 表示 b + 1 i 位数字)
    然后再写个高精度即可。
    2019/7/17 Wednesday 清华大学——赵和旭 27
    基本上一般简单的dp题,就是先想想状态,然后再看能不能转移,如果不能转移,换种设状态的方式,或者增加一下状态的维数,使得局面描述的更加详细。


    2019/7/17 Wednesday 清华大学——赵和旭 28
    最长公共子序列
    给定两个字符串ST,长度分别为nm,求解这两个字符串的最长公共子序列(
    Longest Common Sequence)。
    比如字符串SBDCABA;字符串TABCBDAB
    则这两个字符串的最长公共子序列长度为4,最长公共子序列是: BCBA
    n,m<=1000
    2019/7/17 Wednesday 清华大学——赵和旭 29
    最长公共子序列
    我们设dp[i][j]表示, S串的第i个前缀和T串的第j个前缀的最长公共子序列。
    分情况:
    如果S[i]==T[j]dp[i][j]=dp[i-1][j-1]+1;
    如果S[i]!=T[j]dp[i][j]=max(dp[i-1][j],dp[i][j-1]);
    最后答案就是dp[n][m]
    2019/7/17 Wednesday 清华大学——赵和旭 30
    最长公共子序列
    做法很简单,主要是思路从何而来?
    可用动态规划求解的问题,一般有两个特征:
    1:最优子结构;(全局最优一定是子问题最优结合而得)
    2:重叠子问题;(也就是如果是原先搜索的话要有不少冗余的计算,通过
    dp我们每一个状态就计算一次,减少了复杂度,记忆话搜索本质是一样的)

    本质来说我们就是要想办法把问题化小。
    2019/7/17 Wednesday 清华大学——赵和旭 31
    最长公共子序列
    对于dp[i][j]
    如果,两个串最后一个位置相同,这两个位置一定在公共子序列中。
    那么我们只需要求出Si-1前缀和Tj-1前缀的最长上升子序列就可以了,而这个就是把问题化小。

    如果最后一个位置不相同,那么两个位置一定不能匹配,所以肯定是另外两种情况选最大的。

    2019/7/17 Wednesday 清华大学——赵和旭 32
    记忆化搜索
    2019/7/17Wednesday

    清华大学——赵和旭 33

    网格图路径计数
    给出一个n*m的网格,每次只能向右或者向下走,求从(1,1)走到(n,m)的方案数,其中有些位置是不能走的。

    n,m<=1000
    2019/7/17 Wednesday 清华大学——赵和旭 34
    网格图路径计数
    我们从另一个角度来思考这个问题。
    我们用搜索算法来计算答案。
    然而搜索的时间复杂度是指数级的。
    观察一下:这是有些重复计算的。
    2019/7/17 Wednesday 清华大学——赵和旭 35
    网格图路径计数
    我们发现在这个dfs的过程中, dfs出来的值只与带入参数,也就是(x,y)有关,而不同的
    (x,y)N*M个,而我们之前搜索的问题在于有大量的重复计算,多次调用同一个
    (x,y),每次都从新计算。
    有一个很直观的想法就是, 第一次调用的时候就把答案记下来,之后调用不重新算,直接返回之前已经计算出的答案即可。


    ——这就是记忆化搜索。
    2019/7/17 Wednesday 清华大学——赵和旭 36
    滑雪
    给定一个区域,由一个二维数组给出。数组的(i,j)代表点(i,j)的高度。我们要找一个最长的滑雪路径,注意滑雪只能从高往低处滑。下面是一个例子。


    1 2 3 4 5
    16 17 18 19 6
    15 24 25 20 7
    14 23 22 21 8
    13 12 11 10 9
    2019/7/17 Wednesday 清华大学——赵和旭 37
    解释:一个人可以从某个点滑向上下左右相邻四个点之一,当且仅当高度减小。在上面的例子中,一条可滑行的滑坡为

    24-17-16-1。当然
    25-24-23-...-3-2-1更长。事实上,这是最长的一条。

    滑雪
    dp[x][y]存储在当前位置下山的最大长度,它等于 它旁边的(上下左右)比它矮的山的
    dp值加1 的最大值,即
    dp[x][y]=max(dp[x-1][y] , dp[x][y-1] , dp[x][y+1] , dp[x+1][y])+1
    要保证对应的高度小于H[x][y]才能取max
    1:一般递推式动态规划还要注意枚举状态的顺序,要保证算当前状态时子状态都已经算完了。

    2:但是记忆化搜索不需要,因为记忆化搜索就是个搜索,只不过把重复的部分记下来了而已。我们不用像递推一样过于关注顺序,像搜索一样直接要求什么,调用什么就好。


    2019/7/17 Wednesday 清华大学——赵和旭 38
    滑雪:代码实现
    2019/7/17 Wednesday 清华大学——赵和旭 39
    dfs函数
    主函数部分
    记忆化搜索
    在有一些dp问题中,状态之间的转移顺序不是那么确定,并不能像一些简单问题一样写几个
    for循环就解决了。
    我们可以直接计算最终要求的状态,然后在求这个状态的过程中,要调用哪个子状态就直接调用即可,但是每一个状态调用一遍之后就存下来答案,下次计算的时候就直接取答案即可,就不需要从新再计算一遍。


    虽然看上去每一次都计算不少,但是因为每一个状态都计算一次,所以均摊下来,复杂度还是状态数
    *状态转移。
    2019/7/17 Wednesday 清华大学——赵和旭 40
    拓扑图DP
    2019/7/17Wednesday

    清华大学——赵和旭 41

    拓扑图的dp
    拓扑图dp通常是在拓扑图上求关于所有路径的某种信息之和。当然这里的“和”的运算法则可以是加法或是取
    maxmin。或者其他定义的运算。
    按拓扑序沿着有向边转移就可以了。
    2019/7/17 Wednesday 清华大学——赵和旭 42
    BZOJ4562 食物链
    给定n个点m条边的有向无环食物网,求其中有多少条极长食物链。
    n<=10^5m<=2*10^5
    2019/7/17 Wednesday 清华大学——赵和旭 43
    BZOJ4562 食物链
    拓扑图dp经典题
    f[u]为以节点u为终点的食物链数量。
    按照拓扑序的顺序转移即可。
    2019/7/17 Wednesday 清华大学——赵和旭 44
    食物链:代码实现
    上面的式子是求一个点时,枚举其所有入边转移,具体写代码的时候,我们一般就只记出边再记录一个入边太麻烦了。所以我们一般不枚举入边,而是枚举出边从每个点往后更新,也就是在上面的式子中,我们对于每个





    v向后进行更新,枚举v
    出边指向的所有u点,然后f[u]+=f[v]
    2019/7/17 Wednesday 清华大学——赵和旭 45
    拓扑图dp
    其实我们对于一般非有关期望和概率的dp,如果题目中每一个转移关系是双边的,那么如果我们把
    dp的每一个状态记为一个点, dp状态之间关系构成的图就是一个拓扑图。

    拓扑图dp实际上就是已经给了我们这个拓扑关系了,也就不需要我们自己找了,其实是更简单。

    2019/7/17 Wednesday 清华大学——赵和旭 46
    背包DP模型
    清华大学 赵和旭

    背包dp
    一般是给出一些“物品,每个物品具有一些价值参数和花费参数,要求在满足花费限制下最大化价值或者方案数。

    最简单几种类型以及模型
    0/1背包
    完全背包
    多重背包
    2019/7/17 Wednesday 清华大学 赵和旭 48
    0/1背包问题
    给出n个物品,每个物品有Vi的价值和Wi的费用,我们总共有m块钱,求最多能得到多少价值的物品。

    N<=10^3,m<=10^3
    2019/7/17 Wednesday 清华大学 赵和旭 49
    0/1背包问题代码实现
    2019/7/17 Wednesday 清华大学 赵和旭 50
    更简便更省空间更常用的写法
    0/1背包问题
    dp[i][j]表示前i个物品,用了j的体积得到的最大的价值。
    dp[i][j]=max{dp[i-1][j] , dp[i-1][j-w[i]]+v[i]}
    复杂度O(N^M)
    如果要记录方案数呢?
    如果要求输出方案呢?
    2019/7/17 Wednesday 清华大学 赵和旭 51
    完全背包
    每一个物品可以选无限个。
    dp[i][j]=max{dp[i][j-w[i]],dp[i-1][j]}
    在这里提一下一个贪心的预处理
    对于所有 Vi≥VjCi≤Cj 的物品 i,都可以完全扔掉
    对于体积相同的物品只需要留下价值最大的物品
    对于随即数据这个优化的力度非常大。
    2019/7/17 Wednesday 清华大学 赵和旭 52
    完全背包代码实现
    同样也是两种
    更简便更省空间更常用的写法
    2019/7/17 Wednesday 清华大学 赵和旭 53
    多重背包
    对每一个物品,最多能用t[i]次。
    最暴力的方法就是转移的时候枚举这个物品选几个即可。
    dp[i][j] = max{ dp[i-1][j-w[i]*k] + v[i]*k | k<=t[i]}
    复杂度O( N*M*t[i] )
    2019/7/17 Wednesday 清华大学 赵和旭 54
    多重背包O( N*M*t[i] )代码实现
    也是差不多的
    另一种写法同样简单一些
    2019/7/17 Wednesday 清华大学 赵和旭 55
    序列上的DP
    2019/7/17Wednesday

    清华大学——赵和旭 56

    区间上的dp状态设计最基本的形式
    F[i]表示以i结尾的最优值或方案数。
    F[i][k]表示以i结尾附加信息为k的最优值或方案数。
    转移的话往往是枚举上一个断点。
    F[i]=max { F[j]+ w(j+1,i) | j是一个满足转移条件的断点}
    这是最简单的一类序列上的dp
    2019/7/17 Wednesday 清华大学——赵和旭 57
    bzoj1296 粉刷匠
    n条木板要被粉刷,每条木板分为m个格子,每个格子需要被刷成蓝色或红色。

    每次粉刷可以在一条木板上给连续的一段格子刷上相同的颜色。每个格子最多被刷一次。

    问若只能刷k次,最多正确粉刷多少格子。
    n,m<=50k<=2500
    2019/7/17 Wednesday 清华大学——赵和旭 58
    bzoj1296
    如果只有一条木板,那么设g[i][j]表示前i个格子刷j次的最多正确格子
    g[i][j]=max{ g[k][j-1]+w(k+1,i) | k<i }
    w(x,y)为第x到第y个格子的最多同色格子数,哪个颜色出现的多刷哪个,直接记一个前缀和即可。

    有多条木板,设f[i][j]表示前i个木板刷j次的最大答案。
    f[i][j]=Max{ f[i-1][k]+gi[m][j-k] | k<=j }
    其实像这种一般的dp,就是把影响答案的信息用多维状态来表示,什么必要什么就放在状态里。

    2019/7/17 Wednesday 清华大学——赵和旭 59
    区间DP
    2019/7/17Wednesday

    清华大学——赵和旭 60

    区间dp状态设计的一般形式
    区间dp一般就是设dp[i][j]表示区间[i,j]所能形成的最优答案或者方案数。
    或者像序列一样,多加几维表示附加的信息。
    2019/7/17 Wednesday 清华大学——赵和旭 61
    最简单的区间dp:合并石子
    n堆石子,每次只能合并相邻的两堆石子,并且消耗相邻两堆石子个数的体力值,问最少消耗多少体力值将
    n堆石子合并为1堆。
    N<=100
    2019/7/17 Wednesday 清华大学——赵和旭 62
    合并石子
    dp[i][j]表示将区间[i,j]这段区间内的石子合并为1堆的最小体力值。
    答案就是dp[1][n]
    转移,考虑对于区间[i,j]它一定是由两段区间[i,k][k+1,j]合并成的,所以转移就考虑枚举
    [i,j]区间内的一个分割点k转移即可。
    dp[i][j]=min(dp[i][k]+dp[k+1][j] | i<=k<j)+sum[i,j]
    2019/7/17 Wednesday 清华大学——赵和旭 63
    一定要注意区间dp的枚举顺序!

  • 相关阅读:
    Do You See Me? Ethical Considerations of the Homeless
    ELDER HOMELESSNESS WHY IS THIS AN ISSUE?
    Endoflife support is lacking for homeless people
    html内联框架
    html字体
    html块 div span
    html列表
    html表格
    SQL Server管理员专用连接的使用   作为一名DBA,经常会处理一些比较棘手的服务无响应问题,鉴于事态的严重性,多数DBA可能直接用“重启”大法,以便尽快的恢复生产环境的正常运转,但是多数情况
    如何配置最大工作线程数 (SQL Server Management Studio)
  • 原文地址:https://www.cnblogs.com/liumengliang/p/11199798.html
Copyright © 2011-2022 走看看