恰好 和 不超过
切割次数不超过k次和恰好切割k次的区别仅仅在于dp[i][j],j>=i时的定义不同,一个是无效值,一个是有效值(有点像背包要求装满或不要求转满时的最优解)
所以装满和不要求装满,恰好k次和不超过k次的 主要递推式其实是一样的,区别只在于初始化时,对相关变量的赋值不同
如果是恰好切割k次,则对于j>=i,即长度为i的钢管一定切不了大于长度i的j次,所以它是一个无效解,且因为要求价值最大,所以赋值无效值为-inf
如果是切割次数不超过k次,则对于j>=i,即长度为i的钢管切割不超过大于长度i的j次是有意义的,因为最多只能切割i次,所以此时的dp[i][j]不是无效值而是r(n,n)
但有趣的是他们主要的递推方程都是一样的,
r(n,k)=max(p[i]+r(n-i,k-1) ,1=<i<=n,因为这里的i是可以等于n的所以第一次切n就相当于没有切
DP典例
- 最长递增子序列LIS->Asc[i](设为以a[i]结尾的最长子序列,因为添加下一个元素时必须看前一个末尾元素是否必现在的大)
-Asc[i]=max(Asc[j])+1(j<i&&a[j]<=a[i]) - 最长公共子序列LCS->dp[i][j](比较两个字符串末尾的两个字符)
i=0||j=0 dp[i][j]=0
s[i]==s[j] dp[i][j]=dp[i-1][j-1]+1
s[i]!=s[j] dp[i][j]=max(dp[i][j-1],dp[i-1][j])
最长公共子序列变题
考虑最后一个字符的同时还要考虑是特殊字符的前面一个字符的相等情况
-
正则表达式
-
最长回文子序列->dp[i][j](比较一个字符串的两个端点字符是否相同)
i==j dp[i][j]=1
//s[i]==s[j] dp[i][j]=dp[i-1][j-1]+2
s[i]==s[j] dp[i][j]=dp[i+1][j-1]+2
//s[i]!=s[j] dp[i][j]=max(dp[i][j-1],dp[i-1][j-1])
s[i]!=s[j] dp[i][j]=max(dp[i][j-1],dp[i+1][j]) -
矩阵链->dp[i][j]
-
最优二叉搜索树->dp[i][j]
-
石子合并
dp[i][j]=max(dp[i][k]+dp[k+1][j]+sum(i,j))
考虑最后两个数而不是考虑第一步是怎么选,因为只考虑第一步无法将整个集合分为左右两个独立的子问题,因为他们可能还可以和第一步合成的新数进行下一步合并
所以考虑最后一步的两个数,最后一步的两个书一定是由两个独立的子块各自合并得来的
对于相邻元素合并等问题都可以考虑成最后一步两个独立元素的的合并!!!
所以对于区间dp除了考虑第一步外还可以考虑最后一步
- 编辑距离
dp[i][j]指把**word1[0…i - 1]转换为word2[0…j - 1] **的最小操作数(word1->word2)。
边界条件:
dp[i][0] = i; 从长度为 i 的字符串转为空串 要删除 i 次
dp[0][j] = j. 从空串转为长度为 j 的字符串 要添加 j 次
一般情况:
如果word[i - 1] == word2[j - 1],则dp[i][j] = dp[i - 1][j - 1],因为不需要进行操作,即操作数为0.
如果word[i - 1] != word2[j - 1],则需考虑三种情况,取最小值:
Replace word1[i - 1] by word2[j - 1]: (dp[i][j] = dp[i - 1][j - 1] + 1 (for replacement));
Delete word1[i - 1]: (dp[i][j] = dp[i - 1][j] + 1 (for deletion));
Insert word2[j - 1] to word1[0…i - 1]: (dp[i][j] = dp[i][j - 1] + 1 (for insertion)).
就算末尾两个字符相等,也不是说就一定要匹配,也可以不匹配再添加一个相同的去匹配,让i去匹配j-1.如bcacc,bcac
注意编辑距离初始化时不是把全部0行,0列都初始化为0的,而是初始化为0,1,2,…,n
- 删除k个以内的字符的不同字符串个数->dp[n][k]
dp[i][j]=dp[i-1][j](末尾为a[i]前面再删j个以内)+dp[i-1][j-1](末尾不为a[i]前面再删j-1个以内),和切钢管相同因为末尾位置不同,所以不会出现两个dp里面有重复的
但如果末尾为a[i] 可能会有重复字符串,所以之后要减去重复的字符串,即找前面最近和a[i]相等的一个字符,如果两者之间的距离小于删除的字符数则-dp[i-d-1][j-d],即
dp[k−1][j−(i−k)](a[k]==a[i]) - n个任务分成k块,要使k块里面最大的数最小
s[i,j]= s[i]+s[i+1]+…+s[j], (s[i,j]=0, if i>j)
ps:
- 奇怪的打印机(直接比较两个端点)
- 最长有效括号
在考虑最后一个字符的同时考虑倒数第二个字符
s[i]=’)’&&(s[i-1]‘)’) dp[i]= dpi-1]+dp[i - dp[i-1]-2]+2(和i-1前面已经匹配好的前一个’(‘进行匹配)
s[i]=’)’&&(s[i-1]’(’) dp[i]= dp[i-2]+2(直接匹配)
- lc740. 删除并获得点数(打家劫舍变题)
变删除相邻数值问题为不能抢劫相邻的房间价值最大问题
首先需要变数值为下标及房间编号