[学习笔记-C++篇]day21 动态规划

只要按照学习的时间统计天数就保证是连续学习的欺诈术…


1.算法入门_动态规划

1.1 跳台阶

参考:跳台阶|官方|精华题解

题解没问题,3种方法都很好。

法一:递归

有的时候正着枚举有很多,但是倒着推,就发现只有2种情况。

class Solution {
public:
    int jumpFloor(int number) {
        if(number<=1) return 1;
        return jumpFloor(number-1)+jumpFloor(number-2);
    }
};

法二:记忆化搜索

先从上向下递归,再从下向上回溯。

如果有重复递归,就直接调用结果。

class Solution {
public:
    int jumpFloor(int number) {
        int f[50]{0};//注意初始化方法
        if(number<=1) return 1;
        if(f[number]>0) return f[number];
        return jumpFloor(number-1)+jumpFloor(number-2);
    }
};

法三:动态规划

直接从下向上累加。
但核心还是要先确定每个动态转移方程:f[i]=f[i-1]+f[i-2]

class Solution {
public:
    int dp[50]{0};
    int jumpFloor(int number) {
        dp[0]=1,dp[1]=1;
        for(int i=2;i<=number;i++)
        {
            dp[i]=dp[i-1]+dp[i-2];
        }
        return dp[number];
    }
};

1.2 最长公共子串

参考:【数据结构和算法】动态规划解最长公共子串

突然发现动态规划用矩阵的话,那么状态转移方程一定会涉及到2个维度的量,在这道题一样的,是一个变形的向右向下查找问题。

主要思路:
dp矩阵的每个元素代表2个string分别在第ij个元素结尾的时候公共子串的长度。转移方程:dp[i][j]=dp[i-1][j-1]+1。省去初始化赋0,同时更直观代表元素位置,dp2个维度分别多1。

class Solution {
  public:
    /**
     * longest common substring
     * @param str1 string字符串 the string
     * @param str2 string字符串 the string
     * @return string字符串
     */

    string LCS(string str1, string str2) {
        // write code here
        //int index,mmax;
        int m = str1.size(), n = str2.size();
        vector<vector<int>> dp(m + 1, vector<int> (n + 1, 0));
        int index, len = 0;

        for (int i = 0; i < m; i++) {
            for (int j = 0; j < n; j++) {
                if (str1[i] == str2[j]) {
                    dp[i + 1][j + 1] = dp[i][j] + 1;
                    if (dp[i + 1][j + 1] > len) {
                        len = dp[i + 1][j + 1];
                        index = i;
                    }
                }
            }
        }
        
        string str;
        str.assign(str1,index-len+1,len);
        return str;
    }
};

1.3 矩阵的最小路径和

大佬给出递归和动态规划2种思路。

参考:题解 | #矩阵的最小路径和#

动态规划核心还是确定动态转移方程。

这里可以想到,每一处的最短路径,是上面或者左边的最短路径+当前元素路径长。

那么就可以构建dp[i][j]=min(dp[i-1][j],dp[i][j-1])+matrix[i][j]

第二大问题是初始化。

为什么要初始化,可以这样想:当我要确定第[i][j]个位置的最短路径时,要不然就要找它上面的,要不然就找左边的最短路径,那么极端情况就是最上一行和最左一列,因此需要首先初始化这2部分。而最上一行的上一步只能是左边路径,最左一列的上一步只能是上边路径。所以分别构建dp[0][j]=dp[0][j-1]+matrix[i][j]dp[i][0]=dp[i-1][0]+matrix[i][j]。而这两者最终都会追溯到起始点dp[0][0],所以这个也要初始化。

INT_MAX = 2^31 -1,INT_MIN= -2^31

class Solution {
  public:
    /**
     *
     * @param matrix int整型vector<vector<>> the matrix
     * @return int整型
     */
    int minPathSum(vector<vector<int> >& matrix) {
        // write code here
        int r = matrix.size(), c = matrix[0].size(); //取行和列
        vector<vector<int>> dp(r, vector<int> (c));

        //初始化,需要确定左上,最上行,最左列
        dp[0][0] = matrix[0][0];

        for (int i = 1; i < c; i++) {//初始化最上行
            dp[0][i] = dp[0][i - 1] + matrix[0][i];
        }
        for (int j = 1; j < r; j++) {
            dp[j][0] = dp[j - 1][0] + matrix[j][0];
        }

        for (int i = 1; i < r; i++) {
            for (int j = 1; j < c; j++) {
                dp[i][j] = min(dp[i - 1][j], dp[i][j - 1]) + matrix[i][j];
            }
        }
        return dp[r-1][c-1];
    }
};

大佬的优化,先搬运,再解读。

来解读了,主要是考虑到不往回走,所以每一处的值是可以被重新覆盖的。

# 搬运代码
class Solution {
public:
    /**
     * 
     * @param matrix int整型vector<vector<>> the matrix
     * @return int整型
     */
    int minPathSum(vector<vector<int> >& matrix) {
        int row = matrix.size(), col = matrix[0].size(); 
        for (int i = 0; i < row; i ++) {
            for (int j = 0; j < col; j ++) {
                if (i == 0 && j != 0) { // 数组第一行
                    matrix[0][j] += matrix[0][j - 1];
                } else if (j == 0 && i != 0) { // 数组第一列
                    matrix[i][0] += matrix[i - 1][0]; 
                } else if (i > 0 && j > 0) {
                    // 两种方式取最小的
                    matrix[i][j] += min(matrix[i - 1][j], matrix[i][j - 1]);
                }
            }
        }
        return matrix[row - 1][col - 1];
    }
};

1.4 接雨水问题

三指针目前最清晰。

参考:【数据结构和算法】双指针求接雨水问题,图文结合

class Solution {
  public:
    /**
     * max water
     * @param arr int整型vector the array
     * @return long长整型
     */
    long long maxWater(vector<int>& arr) {
        // write code here
        int s = arr.size();
        if (s <= 2) return 0;

        int left = 0, right = s - 1, mid, high = 0;
        //bool judge=true;
        long long rain = 0;

        for (int i = 0; i < s; i++) {
            if(arr[i]>high)
            {
                high=arr[i];
                mid=i;
            }
        }

        for (int i = left + 1; i < mid; i++) {
            if (arr[i] < arr[left]) rain += (arr[left] - arr[i]);
            else
                left = i;
        }

        for (int i = right - 1; i > mid; i--) {
            if (arr[i] < arr[right]) rain += (arr[right] - arr[i]);
            else
                right = i;
        }

        return rain;
    }
};

双指针暂时理解不到位。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值