关于dfs和dp的思考

一、问题

问题1:给几个数字,输出排列的种树;或者是八皇后问题

问题2:给定一字符串,字符串里有*,*可以换成0或者1,输出各种可能情况。(比如说,字符串是01*78aljdou*a,可以变成01178aljdou1a、01078aljdou1a、01078aljdou0a、01078aljdou0a四种情况。

问题3:输入为字符串,是一串数字,输出各种可能的ip地址(leetcode上Restore IP Addresses)

问题4:在leetcode上一道题,decode ways。  'A' -> 1    'B' -> 2    ...      'Z' -> 26

问题5:给一个数字,如果是奇数,可以加一和减一操作;如果是偶数,可以除以2。我们最终要让这个数变为一,最少的步数是多少?

问题6:给定一串字符串,再给一个字典,将字符串分解为字典中存在的单词,并将各种情况打印出来。

二、dfs

    面对上面这些题时,你仔细比较一下,都是输出各种可能情况,可以看做一种排列问题。怎么用dfs解决这种问题呢?    首先就用问题2举个例子,当访问到*的时候,我需要解决当前问题,可以把*换成0和1两种情况,剩下的问题又成了独立的问题,再递归调用处理剩下的问题。稍微用朴素的语言归纳一下,可以先枚举当前情况,再递归解决后面各种出现的情况.最后写一个伪代码吧,来说明这种思路。

dfs(整个问题){
              取当前一部分,枚举当前情况,并解决
      dfs(剩下的问题)
}

三、dp

    在解决上面的第3、4个问题时,你会发现,有很多重复计算,所以说要保留重复计算的部分,我可以把它分解为一个个子问题,怎么把一个子问题编程再大一点的问题呢?这就要寻找一种大问题与小问题之间的递推关系。下面就用第四个问题来举例,分析一下怎么得到这个递归关系。
    以问题3为例,首先,从头到尾扫这个String,从第一位到dp[i]这一位组成的String,有多少种解码组合。( 保存重复运算子问题
    可以有两种情况:( 分解子问题)1)只解析当前字符,有dp[i-1]种;2)解析i-1和i时,那么dp[i-2]种

    那么递归关系得到dp[i]=dp[i-1]+dp[i-2];(求解最优子结构,这里当然没有通过比较最大最小得到最优,很自然的得到递推)(形式有点类似斐波那契数列,http://blog.csdn.net/qomoman/article/details/38351041)

    同样,在问题5中,同样有类似的递推关系,我们可以分析子问题。

3.1 dp总结

    我觉得可以写成伪代码,如下
分解子问题,子问题求解,并且保存子问题的解,防止重复运算
从众多子问题中,并选取最优的子问题解,得到大问题与子问题的递推关系
    从伪代码和上面分析问题的方法中,我们也可以有比较重要的三点:
1)分解子问题,怎么划分子问题?其实是一个分治的思想
2)保存子问题的解,其实就是以空间换时间,防止重复求解
3)最优子结构,比较所有种可能性,得到全局最优

四、dfs和dp的联合使用

    当有些情况下,单用dfs很难求解,dfs容易造成很多重复的求解,而单独用dp,则很难下手时,我们可以用dfs时,边用dp( 将重复求解的子问题保存下来,以时间换空间),比如说问题6,这个题目很好的诠释了dfs和dp的联用。

五、感悟

    看了些动态规划和dfs的题,有上面一点点儿感想,希望多拍砖,多提意见。



你可能感兴趣的:(算法题)