动态规划专题——passage_1
动态规划组成部分:
1:确定状态
— 确定最后一步(最优策略)
— 抽象子问题
2:归纳转移方程
— 不知道且需要的信息,加进状态里
3:初始条件和边界情况
—
4:计算顺序
— 一般为从左到右,从上到下
例一 最值型动态规划(col1.2)
题目描述:
有2 、 5 、 7三种硬币,求最少几枚能拼够一个整数?(3.-2)
状态转移:
f[x] = min { f[x-2] + 1, f[x-5] + 1, f[x-7]+1 } (3.-3)
初始条件: (3.4)
f | ... | -1 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | ... | 27 |
nan | nan | 0 | nan | 1 | nan | 2 | 1 | 3 | ... | 5 |
小笔记:
感觉入门的动态规划问题还是像由递归延伸出来的记忆化搜索。(2.2)
例二 计数型动态规划
题目描述:
[ m , n ]网格,人从(0,0)出发,每步可以向下或向右走一步,有多少种方法走到左下角?
状态转移:
f[i][j] = f[i-1][j] + f[i][j-1]
边界状况:
i = 0 or j = 0 ——f[i][j] = 1
初始条件:
f[0][0] = 1
例三 存在型动态规划
题目描述:
n块石头在x轴 0 ... n-1位置,青蛙想从0跳到n-1,在第i块石头上能跳ai,问能否跳到n-1上?
状态转移:
f[i] = OR(任意)0<=i<j(f[i] AND i + a[i] >= j )
例四 序列型动态规划
题目描述:
一排N栋房子,每栋要漆成红、蓝、绿三色之一,相邻不能漆成同色,第i栋房子漆成红、蓝、绿的开销分别是[i][0] 、[i][1] 、[i][2],问漆这些房子的最小花费?
确定状态:
最后一栋房子[n-1]必为三色之一,但确定这一状态还需要知道[n-2]的颜色、知道[n-2]为各种颜色时的最小花费。
边界条件:
f[0][OR] = 0
状态转移:
f[i][0] =min { f[i-1][i] + cost[i-1][0] , f[i-1][2] + cost[i-1][0] } 类比,三个颜色三个方程
例五 划分型动态规划
题目描述:
有段A~Z组成的字母串信息被加密成数字串,A-1,B-2,....Z-26,给定加密后字串S[0...N-1],问有多少种解密方式?
确定状态:
求前[n-1]和[n-2]个字符解谜方式数之和。
状态转移:
f[i] = f[i-1] ( S[i-1]能对应一个字母 ) + f[i-2] ( S[i-2]S[i-1]能对应一个字母 )
边界条件:
f[0] = 1
坐标型动态规划
给定一个序列或网格,需要找到序列中某个/某些子序列或网格中的某条路径
— 某种性质最大/最小
— 基数
— 存在性
动态规划方程f[i]中的下标i表示以ai为结尾的满足条件的子序列的性质,f[i][j]中的下标i,j表示以格子(i,j)为结尾的满足条件的路径的性质
— 最大值/最小值
— 个数
— 是否存在
例六 一维坐标型动态规划
题目描述:最长单调连续子序列
给定a[0],...,a[n-1],找到最长的连续子序列i,i+1,i+2,...,j,使得a[i] < a[i+1] < ... < a[j],或者a[i] > a[i+1] > ... >a[j],输出长度 j - i + 1.
例:输入 [ 5 , 1 , 2 , 3 , 4 ]
输出 4
确定状态:
找最长连续上升子序列,再将序列颠倒所求就是最长连续下降子序列。
设f[j] = 以a[j]结尾的最长连续上升子序列的长
情况一:a[j] ans = 1
情况二:a[j-1] < a[j] ans = ans( a[j-1] ) + 1
状态转移:
f[j] = max{1 , f[j-1] + 1 ( j > 0 AND a[j-1] < a[j] ) }
计算顺序:
f[j] =以a[j]结尾的最长连续上升子序列的长度
max{ f[0] , ... , f[n-1] }
例七 二维坐标型动态规划
题目描述:
给定[m , n]网格,每个格子(i , j)里都有一个非负数A[i][j],求一个从左上角(0 , 0)到右下角的路径,每一步只能向下或者向右走一步,使得路径上的格子里的数字之和最小,输出最小的数字和。
确定状态:
无论何种方式到达右下,最后一步只有两种可能。
状态转移:
f[i][j] = min{ f[i-1][j] , f[i][j-1] } +A[i][j]
初始边界:
f[0][0] = A[0][0] i = 0 OR j = 0,前一步方向唯一
空间优化:滚动数组
我们发现计算时我们只用了两行数组,开数组时只用开 f[0][0...n-1] 和 f[1][0...n-1] 交替赋值使用即可。
例八 上难度了
题目描述:
给定[m , n]网格,每个格子可能为空,也可能有个敌人,也可能有一堵墙。只能在某个空格子里放一个炸弹,炸弹会炸死同行同列的所有敌人,但是不能穿透墙。求最多能炸死几个敌人?
输入:
0 | E | 0 | 0 |
E | 0 | W | E |
0 | E | 0 | 0 |
输出: 3
题目分析:
每个格子上下左右求一下,时间复杂度指数级。TEL不用想。动态规划的思想,这题确实也不是很好想。
确定状态:
将爆炸拆解成四个方向,假设所有格子都能放炸弹,单独计算。
有敌人的格子,格子里敌人被炸死,并继续向上爆炸。
有墙的格子,炸弹不能炸死任何敌人。
则,在(i , j)格放一个炸弹,它向上能炸死的敌人数是:
— (i , j)格为空地:( i-1 , j )格向上能炸死的敌人数
— (i , j)格为敌人:( i-1 , j )格向上能炸死的敌人数 + 1
— (i , j)格为墙:0
最后求四个方向,max{ Up[i][j] + Down[i][j] + Left[i][j] +Right[i][j] }
初始条件:
Up[0][j] = 0,如果(0 , j)格不是敌人
Up[0][j] = 1,如果(0 , j)格是敌人
转移方程:
Up[i][j] = { Up[i-1][j] (如果i , j是空地) OR Up[i-1][j] + 1 (如果i , j是敌人) OR 0 (如果i , j是墙) }
空间优化:
可以开一个两行的Up滚动数组,计算四个方向,等规模ans数组存最后结果。
例九 序列 + 位操作型动态规划
位操作(二进制)
&与, |或, ^异或, !非
逐位操作
题目描述:
给定N,要求输出0 , 1 , ... , N 的每个数的二进制表示里的1的个数
输入:5
输出:[ 0,1, 1, 2, 1, 2 ]
状态转移:
f[i] = f[i>>1] + ( i mod 2 )
动态规划去除重复计算
例十 序列型动态规划
题目描述:
一排N栋房子,每栋要漆成K种颜色之一,相邻不能漆成同色,第i栋房子漆第j种颜色的花费是cost[i][j],问漆这些房子的最小花费?
确定状态:
设油漆前i栋房子并且房子i-1是颜色1,颜色2,...颜色K的最小花费分别为f[i][1],f[i][2],...,f[i][K]
状态转移:
f[i][1] = min{ f[i-1][2] + cost[i-1][1],f[i-1][3] + cost[i-1][1],...,f[i-1][K] + cost[i-1][1] }
K个式子我们不能全写出来,写一个for循环,优化下:
f[i][j] = mink!=j { f[i-1][k] } + cost[i-1][j]
每次求一组元素中除了一个元素之外其他元素的最小值,求最小的两个值即可。
假如最小值是f[i-1][a],次小值是f[i-1][b]
对于j = 1,2,3,...,a-1,a+1,...,K
f[i][j] = f[i-1][a] + cost[i-1][j]
f[i][a] = f[i-1][b] + cost[i-1][a]
例十一 序列型动态规划
题目描述:
有一排N栋房子(0~N-1),房子i里有A[i]个金币,有个贼人想选择一些房子偷金币,他不能偷任何相邻两家的,请问这个贼人最多能偷多少金币?
确定状态:
第N-1栋房子,要么偷,要么不偷。不偷时问题变成了求前N-2栋房子最多能偷多少。
设f[i][0]表示不偷房子 i-1 的前提下,前 i 栋房子中最多能偷多少金币
设f[i][1]表示偷房子 i-1 的前提下,前 i 栋房子中最多能偷多少枚金币
转移方程:
f[i][0] = max { f[i-1][0],f[i-1][1] }
f[i][1] = f[i-1][0] + A[i-1]
发现不偷时 f[i][0] = f[i-1][1] 故存在重复,可以简化成以为:
f[i] = max{ f[i-1] ,f[i-2] + A[i-1] }
初始边界:
f[0] = 0,f[1] = A[0], f[2] = max{ A[0],A[1] }
例十二 例十升级
题目描述:
有一圈N栋房子(0~N-1),房子i里有A[i]个金币,有个贼人想选择一些房子偷金币,他不能偷任何相邻两家的,请问这个贼人最多能偷多少金币?
确定状态:
情况1:没偷房子0,就变成了1~N-1序列问题
情况2:没偷房子N-1,就变成了0~N-2序列问题
环形问题,针对性分析有什么情况,化作序列问题。