代码随想录Day37-动态规划:力扣第188h、309m、714m、300m、674e、718m、1143m题

188h. 买卖股票的最佳时机IV

题目链接
代码随想录文章讲解链接

方法一:动态规划+滚动数组

用时:22m45s

思路

dp数组:dp[i][0]表示第i天没有买入过股票,dp[i][2j+1]表示第i天第j+1次持有股票,dp[i][2j]表示第i天第j+1次不持有股票
状态转移:状态转移跟123m题一样

  • 时间复杂度: O ( n ⋅ k ) O(n \cdot k) O(nk)
  • 空间复杂度: O ( k ) O(k) O(k)
C++代码
class Solution {
public:
    int maxProfit(int k, vector<int>& prices) {
        if (prices.size() == 0) return 0;
        vector<int> dp(2 * k + 1, 0);
        for (int j = 1; j < dp.size(); j += 2) dp[j] = -prices[0];
        for (int i = 1; i < prices.size(); ++i) {
            for (int j = dp.size() - 1; j >= 2; j -= 2) {
                dp[j] = max(dp[j], dp[j - 1] + prices[i]);  // 第i次不持有
                dp[j - 1] = max(dp[j - 1], dp[j - 2] - prices[i]);  // 第i次持有
            }
        }
        return dp[2 * k];
    }
};

看完讲解的思考

无。

代码实现遇到的问题

无。


309m. 最佳买卖股票时机含冷冻期

题目链接
代码随想录文章讲解链接

方法一:动态规划

用时:25m59s

思路

dp数组:dp[i][0]表示第i天持有股票的收支,dp[i][1]表示第i天不持有股票且第i-1天也不持有股票的收支,dp[i][2]表示第i天不持有股票且第i-1持有股票的收支。
状态转移:

  1. dp[i][0]:上一天持有股票,今天继续持有股票,以及前两天不持有股票,今天买入股票,两种情况中的最大值,上一天不持有股票,上上天持有股票的情况下,当天处于冻结期,无法买入股票。即dp[i][0] = max(dp[i - 1][0], dp[i - 1][1] - prices[i])
  2. dp[i][1]:上一天不持有股票,今天继续保持不持有股票,上一天不持有股票有两种情况,所以值为两者中的最大值,即dp[i][1] = max(dp[i - 1][1], dp[i - 1][2])
  3. dp[i][2]:上一天持有股票,今天卖出股票,即dp[i][2] = dp[i - 1][0] + prices[i]
  • 时间复杂度: O ( n ) O(n) O(n)
  • 空间复杂度: O ( n ) O(n) O(n)
C++代码
class Solution {
public:
    int maxProfit(vector<int>& prices) {
        int size = prices.size();
        vector<vector<int>> dp(size, vector<int>(3, 0));  // dp数组
        dp[0][0] = -prices[0];  // dp数组初始化
        for (int i = 1; i < size; ++i) {  // 状态转移
            dp[i][0] = max(dp[i - 1][0], dp[i - 1][1] - prices[i]);
            dp[i][1] = max(dp[i - 1][1], dp[i - 1][2]);
            dp[i][2] = dp[i - 1][0] + prices[i];
        }
        return max(dp[size - 1][1], dp[size - 1][2]);
    }
};

方法二:动态规划+滚动数组

用时:2m54s

思路

在方法一的基础上,dp数组使用一维数组。

  • 时间复杂度: O ( n ) O(n) O(n)
  • 空间复杂度: O ( 1 ) O(1) O(1)
C++代码
class Solution {
public:
    int maxProfit(vector<int>& prices) {
        int size = prices.size();
        vector<int> dp(3, 0);  // dp数组
        dp[0] = -prices[0];  // dp数组初始化
        for (int i = 1; i < size; ++i) {  // 状态转移
            int tmp = dp[0] + prices[i];
            dp[0] = max(dp[0], dp[1] - prices[i]);
            dp[1] = max(dp[1], dp[2]);
            dp[2] = tmp;
        }
        return max(dp[1], dp[2]);
    }
};

看完讲解的思考

无。

代码实现遇到的问题

无。


714m. 买卖股票的最佳时机含手续费

题目链接
代码随想录文章讲解链接

方法一:动态规划

用时:4m11s

思路
  • 时间复杂度: O ( n ) O(n) O(n)
  • 空间复杂度: O ( 1 ) O(1) O(1)
C++代码
class Solution {
public:
    int maxProfit(vector<int>& prices, int fee) {
        int dp0 = -prices[0] - fee, dp1 = 0;
        for (int i = 1; i < prices.size(); ++i) {
            int tmp = max(dp0, dp1 - prices[i] - fee);
            dp1 = max(dp1, dp0 + prices[i]);
            dp0 = tmp;
        }
        return dp1;
    }
};

看完讲解的思考

无。

代码实现遇到的问题

无。


300m. 最长递增子序列

题目链接
代码随想录文章讲解链接

方法一:动态规划

用时:16m37s

思路

对于第i个元素nums[i],以nums[i]开头的最长严格递增子序列(LIS)的长度,等于1加上以nums[i]之后的大于nums[i]的元素为开头的LIS的最大长度,用一个数组记录以每个元素为开头的LIS的最大长度,由后往前遍历,因为前面的LIS长度依赖于后面的值。
得到全部LIS长度后,最大值即为答案。

  • 时间复杂度: O ( n 2 ) O(n^2) O(n2)
  • 空间复杂度: O ( n ) O(n) O(n)
C++代码
class Solution {
public:
    int lengthOfLIS(vector<int>& nums) {
        vector<int> vec(nums.size(), 1);
        int res = 1;
        for (int i = nums.size() - 1; i >= 0; --i) {
            for (int j = i + 1; j < nums.size(); ++j) {
                if (nums[j] > nums[i]) vec[i] = max(vec[i], 1 + vec[j]);
            }
        }
        for (int& n : vec) {
            if (n > res) res = n;
        }
        return res;
    }
};

方法二:贪心+二分查找

用时:15m31s

思路

数组last:last[i]表示长度为i的子序列的最小末尾元素。
贪心思想:为了让子序列尽可能的长,所以子序列的末尾元素要尽可能的小

遍历数组,若当前元素n大于last数组最后一个元素,则递增子序列可以增长,并且n是作为末尾元素,所以将n添加到last数组末尾。若n小于last数组最后一个元素,则递增子序列无法增长,但是可以按照贪心思想更新last数组:找到大于n的第一个数,将其替换成n,由于last是递增数组,所以可以用二分查找。
最后的答案就是last数组的长度。

  • 时间复杂度: O ( n log ⁡ n ) O(n \log n) O(nlogn)
  • 空间复杂度: O ( n ) O(n) O(n)
C++代码
class Solution {
public:
    int lengthOfLIS(vector<int>& nums) {
        vector<int> last;
        last.push_back(nums[0]);
        
        for (int i = 1; i < nums.size(); ++i) {
            int target = nums[i];
            if (target > last.back()) last.push_back(target);
            else if (target < last.back()) {
            	// 二分查找
                int left = 0, right = last.size() - 1;
                while (left <= right) {
                    int mid = left + (right - left) / 2;
                    if (last[mid] < target) left = mid + 1;
                    else right = mid - 1;
                }
                last[left] = target;
            }
        }
        return last.size();
    }
};

看完讲解的思考

无。

代码实现遇到的问题

无。


674e. 最长连续递增序列

题目链接
代码随想录文章讲解链接

方法一:滑动窗口

用时:7m26s

思路
  • 时间复杂度: O ( n ) O(n) O(n)
  • 空间复杂度: O ( 1 ) O(1) O(1)
C++代码
class Solution {
public:
    int findLengthOfLCIS(vector<int>& nums) {
        int left = 0, right = 0;
        int res = 1;
        while (++right < nums.size()) {
            if (nums[right] <= nums[right - 1]) {
                res = max(res, right - left);
                left = right;
            }
        }
        return max(res, right - left);
    }
};

方法二:动态规划

用时:5m7s

思路

dp数组:dp[i]表示以nums[i]为结尾的连续递增子序列的最大长度。
状态转移:如果nums[i]大于nums[i-1],则dp[i] = dp[i-1] + 1;否则dp[i] = 1
最后答案是dp数组中的最大值。由于状态转移只与前一个状态有关,所以可以用一个变量记录前一个状态即可。

  • 时间复杂度: O ( n ) O(n) O(n)
  • 空间复杂度: O ( 1 ) O(1) O(1)
C++代码
class Solution {
public:
    int findLengthOfLCIS(vector<int>& nums) {
        int dp = 1;
        int res = 1;
        for (int i = 1; i < nums.size(); ++i) {
            if (nums[i] > nums[i - 1]) ++dp;
            else dp = 1;
            if (dp > res) res = dp;
        }
        return res;
    }
};

看完讲解的思考

无。

代码实现遇到的问题

无。


718m. 最长重复子数组

题目链接
代码随想录文章讲解链接

方法一:暴力解法

用时:21m15s

思路

逐个子数组遍历,逐个匹配。

  • 时间复杂度: O ( n 3 ) O(n^3) O(n3)
  • 空间复杂度: O ( 1 ) O(1) O(1)
C++代码
class Solution {
public:
    int findLength(vector<int>& nums1, vector<int>& nums2) {
        int idx1 = 0;
        int size1 = nums1.size(), size2 = nums2.size();
        int res = 0;

        while (idx1 < size1) {
            for (int i = 0; i < size2 - res; ++i) {
                if (nums2[i] == nums1[idx1]) {
                    int offset = 1;
                    if (res == 0) res = 1;
                    while (idx1 + offset < size1 && i + offset < size2 && nums1[idx1 + offset] == nums2[i + offset]) {
                        ++offset;
                        res = max(res, offset);
                    }
                }
            }
            ++idx1;
        }
        return res;
    }
};

方法二:动态规划

用时:5m52s

思路

dp数组:二维dp数组,dp[i][j]表示nums1中以nums1[i - 1]为结尾的子数组和nums2中以nums2[j - 1]为结尾的子数组的最长公共子数组的长度。
状态转移:若nums1[i - 1] == nums2[j - 1],则dp[i][j] = dp[i - 1][j - 1] + 1

  • 时间复杂度: O ( m n ) O(mn) O(mn),m为nums1的长度,n为nums2的长度。
  • 空间复杂度: O ( m n ) O(mn) O(mn)
C++代码
class Solution {
public:
    int findLength(vector<int>& nums1, vector<int>& nums2) {
        int size1 = nums1.size(), size2 = nums2.size();
        int res = 0;
        vector<vector<int>> dp(size1 + 1, vector<int>(size2 + 1, 0));
        for (int i = 1; i <= size1; ++i) {
            for (int j = 1; j <= size2; ++j) {
                if (nums1[i - 1] == nums2[j - 1]) dp[i][j] = dp[i - 1][j - 1] + 1;
                res = max(res, dp[i][j]);
            }
        }
        return res;
    }
};

方法三:动态规划+滚动数组

用时:5m3s

思路

方法二空间可以用滚动数组优化。

  • 时间复杂度: O ( m n ) O(mn) O(mn)
  • 空间复杂度: O ( n ) O(n) O(n)
C++代码
class Solution {
public:
    int findLength(vector<int>& nums1, vector<int>& nums2) {
        int size1 = nums1.size(), size2 = nums2.size();
        int res = 0;
        vector<int> dp(size2 + 1, 0);
        for (int i = 1; i <= size1; ++i) {
            for (int j = size2; j >= 1; --j) {
                if (nums1[i - 1] == nums2[j - 1]) dp[j] = dp[j - 1] + 1;
                else dp[j] = 0;
                res = max(res, dp[j]);
            }
        }
        return res;
    }
};

看完讲解的思考

无。

代码实现遇到的问题

无。


1143m. 最长公共子序列

题目链接
代码随想录文章讲解链接

方法一:动态规划

用时:11m12s

思路

dp数组:二维dp数组,dp[i][j]表示text1[:i]和text2[:j]的最长公共子序列的长度。
状态转移:见代码。

  • 时间复杂度: O ( m n ) O(mn) O(mn)
  • 空间复杂度: O ( m n ) O(mn) O(mn)
C++代码
class Solution {
public:
    int longestCommonSubsequence(string text1, string text2) {
        int len1 = text1.length(), len2 = text2.length();
        vector<vector<int>> dp(len1 + 1, vector<int>(len2 + 1, 0));
        
        for (int i = 1; i <= len1; ++i) {
            for (int j = 1; j <= len2; ++j) {
                if (text1[i - 1] == text2[j - 1]) dp[i][j] = dp[i - 1][j - 1] + 1;
                else dp[i][j] = max(dp[i - 1][j], dp[i][j - 1]);
            }
        }
        return dp[len1][len2];
    }
};

看完讲解的思考

无。

代码实现遇到的问题

无。


最后的碎碎念

好多天没刷题了,中秋国庆假疯狂摆烂,然后最近又在忙比赛的事儿。
今天暴刷7题,找找手感,还好生疏太多。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值