DP系列——递推/递归+记忆化搜索

http://120.78.128.11/Problem.jsp?pid=1852

一开始我想,从上往下的话,可以用递归,搜索(dfs)所有可能的路径,代码如下:

int dfs(int i,int j){
    if(i==n)//递归到最后一层时返回 
    return a[i][j];//a[i][j]存图,dp[i][j]存数字总和 
    return dp[i][j]=max(dfs(i+1,j),dfs(i,j+1))+a[i][j];//取向左边和向右边走较大的一个数 
} 

从第一层开始递归到最后一层再从最后一层往回递归,,一共递归2的n次方,显然会T,那么要怎么优化一下呢?

我们看到,在题中第三排的1会被第二排的3和8递归两次,经历了一次重复计算,这时我们可以加入一个判断,使dp[][]初始化为-1,如果判断到dp[i][j]不等于-1,那么这个数就是计算过的,不用再次重复递归了,具体代码如下:

int dfs(int i,int j){
    if(i==n)//递归到最后一层时返回 
    return a[i][j];
    if(dp[i][j]>=0)//dp[][]初始化为-1,如果“有记忆”,就不再递归 
    return dp[i][j]; 
    return dp[i][j]=max(dfs(i+1,j),dfs(i,j+1))+a[i][j];
} 

这样就实现了“记忆化搜索”,每一个点只计算一次dp[i][j],那么最终的复杂度就是n的平方。

同时,也可以按照DP的思想从下往上计算,每次取一行中最大的数,从最后一层一直递推到第一层,因为下一步只能走左下或者右下,所以和递归一样取左右最大的一个数:

for(int j=1;j<=n;j++)
dp[n][j]=a[n][j];//先计算最后一层 
for(int i=n;i>=1;i--){//从倒数第二层一直到第一层 
    for(int j=1;j<=i;j++){
        dp[i][j]=a[i][j]+max(dp[i+1][j],dp[i+1][j+1]);
    }
}

 AC代码:

#include<stdio.h>
#include<algorithm>
using namespace std;

int dp[105][105];
int a[105][105];

int main()
{
    int n;
    scanf("%d",&n);
    for(int i=1;i<=n;i++){
        for(int j=1;j<=i;j++){
            scanf("%d",&a[i][j]);
        }
    }
    for(int j=1;j<=n;j++)
    dp[n][j]=a[n][j];
    for(int i=n;i>=1;i--){
        for(int j=1;j<=i;j++){
            dp[i][j]=a[i][j]+max(dp[i+1][j],dp[i+1][j+1]);
        }
    }
    printf("%d
",dp[1][1]);
    return 0;
}

EOF

原文地址:https://www.cnblogs.com/Untergehen/p/14337283.html