寒假刷题打卡第二十七 & 二十八天 | 动态规划

递归和动态规划都是将原问题拆成多个子问题然后求解,他们之间最本质的区别是,动态规划保存了子问题的解,避免重复计算。
斐波拉契类

  1. 爬楼梯
    答案里面还提到了,矩阵迭代的方法,二刷的时候可以看看。
  2. 打家劫舍 I
    f(n) = max(num[n]+f(n-2), num[n-1]+f(n-3)) 因为num[n] 和 nums[n-1] 必有一个要被算入
    一开始还是想到的是递归。
    想到递归离动态规划就不远了。建立递推公式,用变量表示出递推公式中等式右边的变量,再写循环。
  3. 打家劫舍 II
    这道题和上一题很相似,但是我没有想出来。看了答案,发现即使是上一题,答案的解法也比我的解法要优雅。
    状态定义:
    转移方程:
    初始状态:
    返回值:
class Solution {
public:
    int robCore(vector<int>& nums, int i, int j)
    {
        int preMax = 0, curMax = 0;
        for(int k=i;k<=j;k++)
        {
            int temp = curMax;
            curMax = max(nums[k]+preMax, curMax);
            preMax = temp;
        }
        return curMax;
    }
    int rob(vector<int>& nums) {
        if(nums.size()==0) 
            return 0;
        if(nums.size()==1)
            return nums[0];
        return max(robCore(nums, 1, nums.size()-1), robCore(nums, 0, nums.size()-2));
    }
};
  1. 有 N 个 信 和 信封,它们被打乱,求错误装信方式的数量。 & 母牛生产问题

矩阵路径问题
5. 矩阵的最小路径和
这正是前天晚上蔚来二面的算法题目。当时第一反应竟然是dfs…
奇怪的是,我今天竟然在没有看答案的情况下独立做出来了??

    int minPathSum(vector<vector<int>>& grid) {
        if(grid.size()==0 || grid[0].size()==0)
            return 0;
        vector<int> temp(grid[0].size(), 0);
        vector<vector<int>> dp(grid.size(), temp);  //  两条语句可以合并为  vector<vetor<int>> dp(grid.size(), vector<int> (grid[0].size()), 相当于起了一个无名变量
        dp[0][0] = grid[0][0];
        for(int i=1;i<grid[0].size();i++)   //for loop 1
            dp[0][i] = dp[0][i-1] + grid[0][i];
        for(int j=1;j<grid.size();j++)  // for loop 2
            dp[j][0] = dp[j-1][0] + grid[j][0];
        // actually for loop 1 and loop 2 above can be merged into one loop by if sentence
        for(int i = 1;i<grid.size();i++)
            for(int j=1;j<grid[0].size();j++)
                dp[i][j] = min(dp[i-1][j], dp[i][j-1]) + grid[i][j];
        return dp[grid.size()-1][grid[0].size()-1];
  1. 不同路径
    yeah~ one submit, one 100%

数组区间类问题
7. 区域和检索
前两天每日一题中做过,此处就不做记录了。
8. 数组中等差递增子区间的个数
通过这道题有两点收获:
1)一开始我就写出了递推公式(状态转移方程),dp[i]=dp[i-1]+f(i),这不知道是哪根筋不对,就创建了一个vector int dp。简直是违背动态规划的初心。
2)对于(1)中这类等式右边只有一个dp变量的时候,我们也只需要 创建一个变量来做迭代。同理,如果等式右边有两个变量,比如:dp[i]=dp[i-1]+dp[i-2]这种,显然需要两个两个变量。

class Solution {
public:
    int numOfAri(vector<int>& nums, int end)
    {
        int gap = nums[end] - nums[end-1];
        int ans = 0;
        for(int i=end-1;i>=1;i--)
        {
            if(nums[i]-nums[i-1] != gap)  //这个地方一开始写反了:nums[i-1]-nums[i] != gap,
            //显然忘记了
                break;
            ans++;
        }
        return ans;
    }
    int numberOfArithmeticSlices(vector<int>& nums) {
        if(nums.size()<3)
            return 0;
        int n = nums.size();
        int pre = numOfAri(nums, 2);
        for(int i=3; i<n; i++)
        {
            pre = pre + numOfAri(nums, i);
        }
        return pre;
    }
};
  1. 整数拆分
    第一遍:自己不会做,答案看不懂。
    第二遍:
class Solution {
public:
    int integerBreak(int n) {
        vector<int> dp(n+1,0);
        dp[1] = 1;
        for(int i=2;i<=n;i++)
        {
            for(int j=1; j<=i-1;j++)
            {
                dp[i] = max(dp[i], j * (i-j));
                dp[i] = max(dp[i], j * dp[i-j]);
                dp[i] = max(dp[i], dp[j] * (i-j));
                dp[i] = max(dp[i], dp[j] * dp[i-j]);
                //这个地方其实不需要分四种情况讨论,
                //一下一种就足以
                //curMax = Math.max(curMax, Math.max(j * (i - j), j * dp[i - j]));
                //只需要分解其中一个,因为若两个都需要分解的话,定然在扫描之前更小数的时候就已经考虑过了
            }
        }
        return dp[n];
    }
};

  1. 完全平方数
    这道题和第9题类似。
class Solution {
public:
    int numSquares(int n) {
        vector<int> dp(n+1, INT_MAX);
        dp[0] = 1;
        dp[1] = 1;
        for(int i = 2; i<=n; i++)
        {
            if((int)sqrt(i)==sqrt(i))
            {
                dp[i] = 1;
                continue;
            }
            for(int j = 1; j*j<=i/2; j++)  //最终结果肯定是 一个平方数加另一个平方数,所以对于非平方数,可以直接跳过
            {
                dp[i] = min(dp[i], dp[j*j] + dp[i-j*j]);
            }
        }
        return dp[n];
    }
};
  1. 数字解码
    答案牛逼!
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值