• 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

  • 相关阅读:
    深入Spring Security魔幻山谷-获取认证机制核心原理讲解
    灵魂拷问:你真的理解System.out.println()打印原理吗?
    解决分布式React前端在开发环境的跨域问题
    “我以为”与“实际上”
    前端:如何让background背景图片进行CSS自适应
    VSCode如何设置Vue前端的debug调试
    【JDBC】总结
    【JDBC第9章】Apache-DBUtils实现CRUD操作
    【JDBC第8章】数据库连接池
    【JDBC第7章】DAO及相关实现类
  • 原文地址:https://www.cnblogs.com/Untergehen/p/14337283.html
Copyright © 2020-2023  润新知