动态规划系列

基本思想

将一个问题分为子问题递归求解,且将中间结果保存以避免重复计算。通常可以求得子问题的最优解,且最优解的局部也是最优的。求解过程产生多个决策序列,下一步总是依赖上一步的结果,自底向上的求解。

1.揹包问题

0-1揹包问题

有n件物品和一个容量为 v的揹包,放入第i件物品的空间消耗是ci ,得到的价值是wi 。求解将哪些物品装入揹包可使价值总和最大。

基本思路:
定义子问题:f[i][j]表示将前i个物品放入容量为j的揹包中。
求解时只需考虑第i个物品放入或者不放入两种可能
则有状态转移方程:f[i][j] = max{f[i-1][j],f[i-1][j-ci ]+wi

f[0,0...v] = 0
for i <- 1 to n
    for j <- 0 to v
        if(v < c[i]) continue;//放不下第i个物品
        f[i][j] = max(f[i-1][j],f[i-1][v-c[i]]+w[i])

空间优化:
由于 i 在从0 到 n的过程中,每次只以i-1的状态进行计算,我们可以考虑只用一维数组节省空间,即使用f[0…v]来保存状态,但是根据我们的状态转移方程,f[i][j] 是由f[i-1][j]与f[i-1][j-c[i]]来决定的,也就是说考虑往容量大小为j的包里放东西时,取决于i-1的状态时,j-c[i]大小容量的最优解。
j-c[i] < j, 那么在更新j状态的值时,j-c[i]状态的值要没有变过,所以j要从大到小更新。

f[0...v] <- 0
for i <- 1 to n
    for j <- v to c[i]
        f[j] = max(f[j],f[j-c[i]]+w[i])

关于初始化:
如果要求揹包必须装满,那么初始化因为 , 因为如果不要求揹包装满,那么每个子问题必有一个解,什么都不装为0,但如果要求必须装满就可能存在无解的异常状态,用 表示

2.完全揹包问题

同样是有n种物品,一个容量为v的包,但是每种物品可以无限件使用,放入第i种物品费用是ci ,价值是wi ,解价值最大的放法
思路:转化为0-1揹包问题,第i件物品可以无限放,但最多也就放v/ci 件,那么f[i][j]状态更新时的状态转移方程可以为:

f[i][j]=max(f[i1][vkc[i]]+kw[i]),0<=kc[i]<=v

3.最长公共子串

给定两个字符串str1,str2,求最长公共子串
思路:
在比较str1[i]与str2[j]的时候,即以str1[i],str2[j]结尾的子串的最长公共子串,如果str1[i]==str2[j], 结果为str1[i-1]与str2[j-1]的最长公共子串加1,如果用dp[i][j]表示以str1[i],str2[j]结尾的两个字符串的最长公共子串,那么状态转移方程可以写为:

if(str1[i] == str2[j]){
    dp[i][j] = dp[i-1][j-1]+1;
}
else dp[i][j] = 0;

4.最长公共子序列

最长公共子序列与最长公共子串不同,可以不连续
那么在状态转移方程中,在str1[i]与str2[j]如果不等,dp[i][j] 不是为0,而是等于max(dp[i-1][j],dp[i][j-1], 如下:
状态记录二维数组大小可以为dp[n1+1][n2+1], 留dp[0][0]作为初始状态

if(str1[i] == str2[j]){
    dp[i][j] = dp[i-1][j-1]+1;
}
else{
    dp[i][j] = max(dp[i-1][j],dp[i][j-1]);
}

待续

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章