DFS--记忆化DFS--DP之间的联系

我在DP这块一直挺乱的,做2017年校招真题(输出1,2,3...n中和等于m的所有可能组合)的时候,当时用的DFS,做完之后感觉怎么跟DP有点像呢,然后就搜了一下大佬们都怎么看这个问题的,结果确实是有那么一点联系的。

以数字三角形为例:给出一个数字三角形,从顶部到底部有很多路径,求路径最大和。

如:

7
3   8
8   1   0
2   7   4   4

4   5   2   6   5

朴素DFS的想法就是从(0,0)开始求下一步(向下或者右下)的最大值加上当前点的值

 int dfs(int x,int y) //表示从第x行,第y个数往下走可以得到的最大价值和,dfs(0,0)即为本题解。
{
       //递归出口
       if(x > numCount - 1)
              return 0;
       //对于当前(x,y),取往下或者右下最大的,如此类推,(0,0)开始递归得到结果
       return max(dfs(x+1,y),dfs(x+1,y+1)) + num[x][y];
}

可以看出朴素DFS有大量的重复计算,比如对于中间的一点,他既是某一点的下又是另一点的右下。

记忆化DFS就是把上面DFS的结果保存起来,要用的时候直接使,不需要再来一次递归:

//result数组保存中间结果
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];
}

综上,朴素DFS和记忆化DFS都是基于递归思想计算,而利用递归的条件有两个:一是可以把待解决问题转化成一个新问题,而这个新问题的解决方法不变,但是问题规模在减小;二是要有递归出口。

比如这里我们通过dfs(x+1,y)和dfs(x+1,y+1)去求(x,y),x大于行数的时候退出。

DP动态规划直接用数组保存状态,在状态和状态之间实现转移。

//numCount即数组三角形的行数
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];

result数组空间比三角形大1,全初始化为0,Dp的计算过程是先算三角形的最后一行result[numCount-1][j],result[i+1][j]从实际意义讲不存在,这里等于1,所以这一行计算结果都是本身,然后算上一行直到(0,0),得到问题最终解。

可以看出记忆化DFS和DP动态规划都保存了中间结果,不同的是两者的想法正好是反的,DP从下往上算,记忆化DFS因为是递归从上往下算。

你可能感兴趣的:(dp)