从DFS到记忆化DFS

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了从DFS到记忆化DFS相关的知识,希望对你有一定的参考价值。

DFS到记忆化DFS到动态规划

什么是动态规划?

       动态规划(Dynamic Programming)是通过组合子问题的解来解决问题的。动态规划是用于求解包含重叠子问题的最优化问题的方法。其基本思想是,将原问题分解为相似的子问题。在求解的过程中通过子问题的解求出原问题的解。

       动态规划的分类:

1.       线性规划:拦截导弹,合唱队形,挖地雷等。

2.       区域规划:石子合并,加分二叉树,统计单词个数等。

3.       树形动规:贪吃的九头龙,二分查找树,聚会的欢乐等。

4.       背包问题:01背包问题,完全背包问题,分组背包问题,装箱问题,挤牛奶等

5.       除此之外还有插头DP,按位DP,状态压缩DP等等。

入门题目:数字三角形

题目描述:给出了一个数字三角形。从三角形的顶部到底部有很多条不同的路径。对于

每条路径,把路径上面的数加起来可以得到一个和,你的任务就是找到最大的和。

       注意:路径上的每一步只能从一个数走到下一层上和它最近的左边的那个数或者右边的那个数。

       如:

       7

3   8

8   1   0

2   7   4   4

4   5   2   6   5

分析一下为什么不能使用贪心算法,即每一次走都走下一行的两个数值较大的。因为有可能在某一步的时候数值小没走到,但是该路径下面有较大的数值,就不会走到,就得不到最大的值。

1.朴素DFS搜索算法

可以采用朴素的深度优先搜索算法,即朴素DFS。每一次走都分为两步,第一步求出走下一步时走的最大值,然后加上此步的数值。

int dfs(int x,int y) //表示从第x行,第y个数往下走可以得到的最大价值和,dfs(0,0)即为本题解。

{

       if(x > numCount - 1)

              return 0;

       return max(dfs(x+1,y),dfs(x+1,y+1)) + num[x][y];

}

       完整的代码示例为:

#include

using namespace std;

 

int num[5][5] = {7,0,0,0,0,

              3,8,0,0,0,

              8,1,0,0,0,

              2,7,4,4,0,

              4,5,2,6,5};

int numCount = 5;

 

int dfs(int x,int y)

{

    if(x > numCount - 1)

       return 0;

    return max(dfs(x+1,y),dfs(x+1,y+1)) + num[x][y];

}

 

int main()

{

    int maxsum = 0;

    maxsum = dfs(0,0);

    cout<<maxsum<<endl;

    return 1;

}

这个算法效率极低,为什么呢?因为其中有大量的重复计算。通常递归都会有大量的重复计算。

2.DFS+记忆化搜索方法

    针对于上述DFS算法的重复计算,我们可以先将dfs(x,y)的结果保存起来,等到下次需要时,直接使用。这就是记忆化搜索。

int dfs(int x,int y)

{

    if(x > numCount - 1)

       return 0;

    if(result[x][y] != -1)

       return result[x][y];

return result[x][y] = max(dfs(x+1,y),dfs(x+1,y+1)) + num[x][y];

}

完整的代码示例为:

#include

using namespace std;

 

int num[5][5] = {7,0,0,0,0,

3,8,0,0,0,

8,1,0,0,0,

2,7,4,4,0,

4,5,2,6,5};

int numCount = 5;

int result[5][5];

 

int dfs(int x,int y)

{

    if(x > numCount - 1)

        return 0;

    if(result[x][y] != -1)

       return result[x][y];

    return result[x][y] = max(dfs(x+1,y),dfs(x+1,y+1)) + num[x][y];

}

 

int main()

{

    for(int i = 0;i < 5;i++)

       for(int j = 0; j < 5;j++)

           result[i][j] = -1;

    int maxsum = 0;

    maxsum = dfs(0,0);

    cout<<maxsum<<endl;

    return 1;

}

    不论是DFS还是记忆化DFS都是基于递归的思想进行计算。总结一下实际操作的特点。
     (1)搜索的参数只有(x,y),每一对(x,y)确定一个状态。

     (2)搜搜的转移是从(x+1,y)和(x+1,y+1)到(x,y)的,意思就是我们通过(x+1,y)和(x+1,y+1)的解去求(x,y)。

     于是,设想:如果不用DFS,直接用数组保存状态,在状态与状态之间实现转移。

     for(int i = numCount - 1; i >= 0; i--)

       for(int j = 0;j <= i; j++)

           result[i][j] = max(result[i+1][j],result[i+1][j+1]) + num[i][j];

    完整的程序示例为:

#include

using namespace std;

 

int num[5][5] = {7,0,0,0,0,

3,8,0,0,0,

8,1,0,0,0,

2,7,4,4,0,

4,5,2,6,5};

int numCount = 5;

int result[6][6];

 

int main()

{

    for(int i = 0;i < 6;i++)

       for(int j = 0; j < 6;j++)

           result[i][j] = 0;

    for(int i = numCount - 1; i >= 0; i--)

       for(int j = 0;j <= i; j++)

           result[i][j] = max(result[i+1][j],result[i+1][j+1]) + num[i][j];

    int maxsum = 0;

    maxsum = result[0][0];

    cout<<maxsum<<endl;

    return 1;

}

总结:搜索的参数只有(x,y)。每一对(x,y)确定一个状态。我们通过(x+1,y)和(x+1,y+1)的解去求(x,y),于是我们可以设计出状态并明确状态的转移,从而写出状态转移方程。这就是动态规划!!!!

以上是关于从DFS到记忆化DFS的主要内容,如果未能解决你的问题,请参考以下文章

DFS+记忆化剪枝--[NOIP2017 普及组] 棋盘

学生的出勤记录II--动态规划和dfs记忆化解决

记忆化搜索(DFS)--How many ways

poj 1088 滑雪 DP(dfs的记忆化搜索)

[Uva1637][DFS][记忆化] 纸牌游戏 Double Patience

判断环形数组是否存在环--强行dfs记忆化 or 快慢指针解决