[代码随想录]动态规划

动态规划

文章目录

    • 动态规划
      • 509. 斐波那契数
      • 70. 爬楼梯
      • 746. 使用最小花费爬楼梯
      • 62.不同路径
      • 63. 不同路径 II
      • *343. 整数拆分
      • 96.不同的二叉搜索树
      • 416. 分割等和子集
      • 1049.最后一块石头的重量II
      • *494.目标和
      • *474.一和零
      • 518.零钱兑换II
      • 377. 组合总和 Ⅳ
      • 322. 零钱兑换
      • 279.完全平方数
      • 139.单词拆分
      • 198.打家劫舍
      • 213.打家劫舍II
      • 337.打家劫舍 III
      • 121. 买卖股票的最佳时机
      • 122.买卖股票的最佳时机II
      • *123.买卖股票的最佳时机III
      • *188.买卖股票的最佳时机IV
      • *309.最佳买卖股票时机含冷冻期
      • 714.买卖股票的最佳时机含手续费
      • 300.最长递增子序列
      • 674. 最长连续递增序列
      • *718. 最长重复子数组
      • 1143.最长公共子序列
      • 1035.不相交的线
      • 53. 最大子序和
      • 392.判断子序列
      • *115.不同的子序列
      • 583. 两个字符串的删除操作
      • 72. 编辑距离
      • 647. 回文子串
      • 516.最长回文子序列

动态规划五步曲

  1. 确定dp数组(dp table)以及下标的含义
  2. 确定递推公式
  3. dp数组如何初始化
  4. 确定遍历顺序
  5. 举例推导dp数组

509. 斐波那契数

斐波那契数,通常用 F(n) 表示,形成的序列称为 斐波那契数列 。该数列由 0 和 1 开始,后面的每一项数字都是前面两项数字的和。也就是: F(0) = 0,F(1) = 1 F(n) = F(n - 1) + F(n - 2),其中 n > 1 给你n ,请计算 F(n) 。

class Solution {
public:
    int fib(int n) {
        int num1 = 0;
        int num2 = 1;
        if(n == 0)return 0;
        if(n == 1)return 1;
        for(int i = 2;i < n;i++){
            int num = num2;
            num2 += num1;
            num1 = num;
        }
        return num1 + num2;
    }
};

70. 爬楼梯

假设你正在爬楼梯。需要 n 阶你才能到达楼顶。

每次你可以爬 1 或 2 个台阶。你有多少种不同的方法可以爬到楼顶呢?

注意:给定 n 是一个正整数。

class Solution {
public:
//1.可以用笔纸列出规律
//2.可以想象,到n阶,只有从n-1上来,或者从n-2上来,所以到n-1阶的方法加上n-2阶方法加在一起就是上n阶办法
    int climbStairs(int n) {
        int d1 = 1;
        int d2 = 2;
        if(n <= 3)return n;
        for(int i = 3;i <= n;i++){
            int d3 = d2;
            d2 += d1;
            d1 = d3;
        }
        return d2;
    }
};
class Solution {
public:
    int climbStairs(int n) {
        vector<int>dp(n + 1,0);
        dp[0] = 1;
        for(int i = 1;i <= n; i++){
            for(int j = 1;j <= 2;j++){
                if (i - j >= 0)dp[i] += dp[i - j];
            }
        }
        return dp[n];
    }
};

746. 使用最小花费爬楼梯

给你一个整数数组 cost ,其中 cost[i] 是从楼梯第 i 个台阶向上爬需要支付的费用。一旦你支付此费用,即可选择向上爬一个或者两个台阶。

你可以选择从下标为 0 或下标为 1 的台阶开始爬楼梯。

请你计算并返回达到楼梯顶部的最低花费。

class Solution {
public:
    int minCostClimbingStairs(vector<int>& cost) {
        if(cost.size() <= 2)return min(cost[0],cost[1]);
        for(int i = 2;i < cost.size();i++){
            cost[i] += min(cost[i - 1],cost[i - 2]);
        }
        int sum = min(cost[cost.size() - 1],cost[cost.size() - 2]);
        return sum;
    }
};

/*代码随想录的,也可以用公式法
class Solution {
public:
    int minCostClimbingStairs(vector& cost) {
        vector dp(cost.size() + 1);
        dp[0] = 0; // 默认第一步都是不花费体力的
        dp[1] = 0;
        for (int i = 2; i <= cost.size(); i++) {
            dp[i] = min(dp[i - 1] + cost[i - 1], dp[i - 2] + cost[i - 2]);
        }
        return dp[cost.size()];
    }
};
*/

62.不同路径

一个机器人位于一个 m x n 网格的左上角 (起始点在下图中标记为 “Start” )。

机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角(在下图中标记为 “Finish” )。

问总共有多少条不同的路径?

class Solution {
public:
    int uniquePaths(int m, int n) {
        if(m < 2 && n < 2)return 1;
        vector<vector<int>>d(m,vector<int>(n,0));
        for(int i = 0;i < m;i++){
            for(int j = 0;j < n;j++){
                if(i == 0 || j == 0){
                    d[i][j] = 1;
                }
                else{
                    d[i][j] = d[i - 1][j] + d[i][j - 1];
                }   
            }
        }
        return d[m - 1][n - 1];
    }
};
//也可以用深度搜索和数学方法

63. 不同路径 II

一个机器人位于一个 m x n 网格的左上角 (起始点在下图中标记为“Start” )。

机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角(在下图中标记为“Finish”)。

现在考虑网格中有障碍物。那么从左上角到右下角将会有多少条不同的路径?

class Solution {
public:
    int uniquePathsWithObstacles(vector<vector<int>>& obstacleGrid) {
        int m = obstacleGrid.size();
        int n = obstacleGrid[0].size();
        vector<vector<int>>d(m,vector<int>(n,0));
        for(int i = 0;i < m;i++){
            if(obstacleGrid[i][0] == 1)break;
            else d[i][0] = 1;
        }
        for(int i = 0;i < n;i++){
            if(obstacleGrid[0][i] == 1)break;
            else d[0][i] = 1;
        }
        for(int i = 1;i < m;i++){
            for(int j = 1;j < n;j++){
                if(obstacleGrid[i][j] == 1)continue;
                else{
                    d[i][j] = d[i - 1][j] + d[i][j - 1];
                }
            }
        }
        return d[m - 1][n - 1];
    }
};

*343. 整数拆分

给定一个正整数 n ,将其拆分为 k正整数 的和( k >= 2 ),并使这些整数的乘积最大化。

返回 你可以获得的最大乘积

class Solution {
public:
//代码随想录
    int integerBreak(int n) {
        if(n == 2)return 1;
        vector<int>dp(n + 1,1);
        for(int i = 3;i <= n;i++){
            for(int j = 1;j < i - 1;j++){
                dp[i] = max( max((i - j) * j, dp[i - j] * j));
            }
        }
        return dp[n];
    }
};

96.不同的二叉搜索树

给你一个整数 n ,求恰由 n 个节点组成且节点值从 1n 互不相同的 二叉搜索树 有多少种?返回满足题意的二叉搜索树的种数。

class Solution {
public:
    int numTrees(int n) {
        if(n <= 2)return n;
        vector<int>dp(n + 1,0);
        dp[1] = 1;
        dp[2] = 2;
        for(int i = 3; i <= n;i++){
            for(int j = 1;j <= i;j++){ 
                if((j - 1) <= 1 && (i - j) <= 1){
                    dp[i] += dp[1];
                }
                else if((j - 1) <= 1 && (i - j) > 1){
                    dp[i] += dp[i - j];
                }
                else if((j - 1) > 1 && (i - j) <= 1){
                    dp[i] += dp[j - 1];
                }
                else if((j - 1) > 1 && (i - j) > 1){
                    dp[i] += (dp[j - 1] * dp[i - j]);
                }
            }
        }
        return dp[n];
    }
};
class Solution {
public:
    int numTrees(int n) {
        if(n <= 2)return n;
        vector<int>dp(n + 1,0);
        dp[0] = 1;
        dp[1] = 1;
        dp[2] = 2;
        for(int i = 3; i <= n;i++){
            for(int j = 1;j <= i;j++){                
                dp[i] += (dp[j - 1] * dp[i - j]);
            }
        }
        return dp[n];
    }
};

416. 分割等和子集

给你一个 只包含正整数非空 数组 nums 。请你判断是否可以将这个数组分割成两个子集,使得两个子集的元素和相等。

class Solution {
public:
    bool canPartition(vector<int>& nums) {
        int sum = 0;
        for(int i = 0;i < nums.size();i++){
            sum += nums[i];
        }
        if(sum % 2 != 0)return false;
        int num = sum / 2;
        vector<int>dp(10001,0);
        for(int i = 0;i < nums.size();i++){
            for(int j = num;j >= nums[i];j--){
                dp[j] = max(dp[j],dp[j - nums[i]] + nums[i]);
            }
        }
        if(dp[num] == num)return true;
         return false;
    }
};
class Solution {
public:
    bool canPartition(vector<int>& nums) {
        int sum = 0;
        for(int i = 0;i < nums.size();i++){
            sum += nums[i];
        }
        if(sum % 2 != 0)return false;
        int num = sum / 2;
        vector<int>dp(num + 1,0);
        for(int i = 0;i < nums.size();i++){
            for(int j = num;j >= nums[i];j--){
                dp[j] = max(dp[j],dp[j - nums[i]] + nums[i]);
            }
        }
        if(dp[num] == num)return true;
         return false;
    }
};

1049.最后一块石头的重量II

有一堆石头,用整数数组 stones 表示。其中 stones[i] 表示第 i 块石头的重量。

每一回合,从中选出任意两块石头,然后将它们一起粉碎。假设石头的重量分别为 xy,且 x <= y。那么粉碎的可能结果如下:

  • 如果 x == y,那么两块石头都会被完全粉碎;
  • 如果 x != y,那么重量为 x 的石头将会完全粉碎,而重量为 y 的石头新重量为 y-x

最后,最多只会剩下一块 石头。返回此石头 最小的可能重量 。如果没有石头剩下,就返回 0

class Solution {
public:
    int lastStoneWeightII(vector<int>& stones) {
        int sum = 0;
        for(int i = 0;i < stones.size();i++){
            sum += stones[i];
        }
        int num = sum / 2;
        vector<int>dp(int(num) + 1,0);
        for(int i = 0;i < stones.size();i++){
            for(int j = num;j >= stones[i];j--){
                dp[j] = max(dp[j],dp[j - stones[i]] + stones[i]);
            }
        }
        return sum - 2 * dp[num];
    }
};

*494.目标和

给你一个整数数组 nums 和一个整数 target

向数组中的每个整数前添加 '+''-' ,然后串联起所有整数,可以构造一个 表达式

  • 例如,nums = [2, 1] ,可以在 2 之前添加 '+' ,在 1 之前添加 '-' ,然后串联起来得到表达式 "+2-1"

返回可以通过上述方法构造的、运算结果等于 target 的不同 表达式 的数目。

class Solution {
public:
    int findTargetSumWays(vector<int>& nums, int target) {
        int sum = 0;
        for(int i = 0;i < nums.size();i++){
            sum += nums[i];
        }
        if(sum < abs(target))return 0;
        if((target + sum) % 2 != 0)return 0;//必须有,不然可能到达x.5数字没有但是到x数字有
        //没想出来
        //看了一点点思路:left组合为加法组合,right为减法组合
        //left + right = target
        //left - right = sum
        //left = (target + sum) /2
        int left = (target + sum) / 2;
        vector<int>dp(left + 1,0);
        dp[0] = 1;
        for(int i = 0;i < nums.size();i++){
            for(int j = left;j >= nums[i];j--){
               dp[j] += dp[j - nums[i]];
            }
        }
        return dp[left];
    }
};

*474.一和零

给你一个二进制字符串数组 strs 和两个整数 m 和 n 。

请你找出并返回 strs 的最大子集的大小,该子集中 最多 有 m 个 0 和 n 个 1 。

如果 x 的所有元素也是 y 的元素,集合 x 是集合 y 的 子集 。

class Solution {
public:
//还要在思考思考
    int findMaxForm(vector<string>& strs, int m, int n) {
        vector<vector<int>>s(strs.size(),vector<int>(2,0));
        for(int i = 0;i < strs.size();i++){
            for(int j = 0;j <strs[i].size();j++){
                if(strs[i][j] == '0')s[i][0]++;
                else s[i][1]++;
            }
        }
        
        //二维数组,列是1,行是0
        vector<vector<int>>dp(n + 1,vector<int>(m + 1,0));
        for(int k = 0;k < s.size();k++){
            for(int i = n;i >= s[k][1];i--){
                for(int j = m;j >= s[k][0];j--){
                    dp[i][j] = max(dp[i][j],dp[i - s[k][1]][j - s[k][0]] + 1);
                }
            }
        }
        return dp[n][m];
    }
};

518.零钱兑换II

给定不同面额的硬币和一个总金额。写出函数来计算可以凑成总金额的硬币组合数。假设每一种面额的硬币有无限个。

class Solution {
public:
    int change(int amount, vector<int>& coins) {
        vector<int>dp(amount + 1,0);
        dp[0] = 1;
        for(int i = 0;i < coins.size();i++){
            for(int j = coins[i];j <= amount;j++){
                dp[j] += dp[j - coins[i]];
            }
        }
        return dp[amount];
    }
};

377. 组合总和 Ⅳ

给你一个由 不同 整数组成的数组 nums ,和一个目标整数 target 。请你从 nums 中找出并返回总和为 target 的元素组合的个数。

题目数据保证答案符合 32 位整数范围。

class Solution {
public:
    int combinationSum4(vector<int>& nums, int target) {
        sort(nums.begin(),nums.end());
        if(nums[0] > target)return 0;

        vector<int>dp(target + 1,0);
        dp[0] = 1;
        for(int i = 0;i <= target;i++){
            for(int j = 0;j < nums.size();j++){
                if(i >= nums[j] && dp[i] < INT_MAX - dp[i - nums[j]]){
                    //测试用例有两个数相加超过int的数据,所以需要在if里加上dp[i]
                    dp[i] += dp[i - nums[j]]; 
                }
               
            }
        }
        return dp[target] ;
    }
};

322. 零钱兑换

给你一个整数数组 coins ,表示不同面额的硬币;以及一个整数 amount ,表示总金额。

计算并返回可以凑成总金额所需的 最少的硬币个数 。如果没有任何一种硬币组合能组成总金额,返回 -1

你可以认为每种硬币的数量是无限的。

class Solution {
public:
    int coinChange(vector<int>& coins, int amount) {
        sort(coins.begin(),coins.end());
        if(amount == 0)return 0;
        if(coins[0] > amount )return -1;
        if(find(coins.begin(),coins.end(),amount) != coins.end())return 1;
        vector<int>dp(amount + 1,0);
        for(int i = 0;i < coins.size();i++){
            for(int j = coins[i];j <= amount;j++){
                if(j - coins[i] > 0 && dp[j - coins[i]] >0 && dp[j] > 0){
                    dp[j] = min(dp[j], dp[j - coins[i]] + 1);  
                }
                else if(j - coins[i] == 0 || dp[j - coins[i]] >0){
                    dp[j] = dp[j - coins[i]] + 1;
                }

            }
        }
        if(dp[amount] == 0)return -1;
        return dp[amount];
    }
};


279.完全平方数

给你一个整数 n ,返回 和为 n 的完全平方数的最少数量

完全平方数 是一个整数,其值等于另一个整数的平方;换句话说,其值等于一个整数自乘的积。例如,14916 都是完全平方数,而 311 不是。

class Solution {
public:
    int numSquares(int n) {
        vector<int>dp(n + 1,INT_MAX);
        dp[0] = 0;
        for(int i = 0;i <= n;i++){
            for(int j = 1;j <= sqrt(i);j++){
                if(dp[i - j * j] != INT_MAX){
                    dp[i] = min(dp[i],dp[i - j * j] + 1);
                }
            }
        }
        return  (dp[n] == INT_MAX) ? -1 : dp[n];
    }
};

// class Solution {
// public:
//     int numSquares(int n) {
//         vectordp(n + 1,INT_MAX);
//         dp[0] = 0;
//         for(int i = 0;i <= n;i++){
//             if(sqrt(i) - (int)sqrt(i) != 0)continue;
//             for(int j = i;j <= n;j++){
//                 if(dp[j - i] != INT_MAX){
//                     dp[j] = min(dp[j],dp[j - i] + 1);
//                 }
//             }
//         }
//         return  (dp[n] == INT_MAX) ? -1 : dp[n];
//     }
// };

139.单词拆分

给定一个非空字符串 s 和一个包含非空单词的列表 wordDict,判定 s 是否可以被空格拆分为一个或多个在字典中出现的单词。

class Solution {
public://*****
    bool wordBreak(string s, vector<string>& wordDict) {
        vector<int>dp(s.size() + 1,0);
        dp[0] = 1;
        for(int i = 1;i <= s.size();i++){
            for(int j = 0;j < i;j++){
                string str = s.substr(j,i - j);
                if(find(wordDict.begin(),wordDict.end(),str) != wordDict.end() && dp[j] > 0){
                    //dp[j]>0表明,前一个字符完整存在,只能覆盖不能重叠,而且不能跳过一开始
                    dp[i] = 1;
                }
            }
        }
        return dp[s.size()];
    }
};

198.打家劫舍

你是一个专业的小偷,计划偷窃沿街的房屋。每间房内都藏有一定的现金,影响你偷窃的唯一制约因素就是相邻的房屋装有相互连通的防盗系统,如果两间相邻的房屋在同一晚上被小偷闯入,系统会自动报警

给定一个代表每个房屋存放金额的非负整数数组,计算你 不触动警报装置的情况下 ,一夜之内能够偷窃到的最高金额。

class Solution {
public:
   int rob(vector<int>& nums) {
       if(nums.size() == 1)return nums[0];
       if(nums.size() == 2)return max(nums[0],nums[1]);
       nums[2] += nums[0];
       if(nums.size() == 3)return max(nums[1],nums[2]);

       for(int i = 3;i < nums.size();i++){
           nums[i] += max(nums[i - 2],nums[i - 3]);
       }
       return max(nums[nums.size() - 1],nums[nums.size() - 2]);
   }
};

213.打家劫舍II

你是一个专业的小偷,计划偷窃沿街的房屋,每间房内都藏有一定的现金。这个地方所有的房屋都 围成一圈 ,这意味着第一个房屋和最后一个房屋是紧挨着的。同时,相邻的房屋装有相互连通的防盗系统,如果两间相邻的房屋在同一晚上被小偷闯入,系统会自动报警 。

给定一个代表每个房屋存放金额的非负整数数组,计算你 在不触动警报装置的情况下 ,能够偷窃到的最高金额。

class Solution {
public:
    int rob(vector<int>& nums) {
        if(nums.size() == 1)return nums[0];
        vector<int>dp1(nums.size(),0);
        vector<int>dp2(nums.size(),0);
        dp1[0] = nums[0];
        dp1[1] = max(nums[0],nums[1]);
        dp2[0] = 0;
        dp2[1] = nums[1];
        //if(nums.size() == 2)return dp1[nums.size() - 1];
        for(int i = 2;i < nums.size();i++){
            dp1[i] = max(dp1[i - 2] + nums[i],dp1[i - 1]);
            dp2[i] = max(dp2[i - 2] + nums[i],dp2[i - 1]);
        }
        return max(dp1[nums.size() - 2],dp2[nums.size() - 1]);
    }
};

337.打家劫舍 III

在上次打劫完一条街道之后和一圈房屋后,小偷又发现了一个新的可行窃的地区。这个地区只有一个入口,我们称之为“根”。 除了“根”之外,每栋房子有且只有一个“父“房子与之相连。一番侦察之后,聪明的小偷意识到“这个地方的所有房屋的排列类似于一棵二叉树”。 如果两个直接相连的房子在同一天晚上被打劫,房屋将自动报警。

计算在不触动警报的情况下,小偷一晚能够盗取的最高金额。

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode() : val(0), left(nullptr), right(nullptr) {}
 *     TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
 *     TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
 * };
 */
class Solution {
public:
    int travel(TreeNode* root){
        if(root == nullptr)return 0;
        int leftval = travel(root->left);
        int rightval = travel(root->right);
        if(leftval == 0 && rightval == 0){
            return root->val;
        }
        if(leftval == 0 && root->right->left == nullptr && root->right->right == nullptr){
            return root->val = max(rightval + leftval,root->val);
        }
        else if(rightval == 0 &&root->left->left == nullptr && root->left->right == nullptr ){
            return root->val = max(rightval + leftval,root->val);
        }
        else if(leftval != 0 && rightval != 0 && root->left->left == nullptr && root->left->right == nullptr && root->right->left == nullptr && root->right->right == nullptr){
            return root->val = max(rightval + leftval,root->val);
        }
        else if(leftval != 0 && rightval == 0){
            if(root->left->left != nullptr){
                root->val =  root->val + root->left->left->val;
            }
             if(root->left->right != nullptr){
                root->val = root->val + root->left->right->val;
            }
            return root->val = max(root->val,rightval + leftval);
        }
        else if(leftval == 0 && rightval != 0){
             if(root->right->left != nullptr){
                root->val =  root->val + root->right->left->val;
            }
             if(root->right->right != nullptr){
                root->val =  root->val + root->right->right->val;
            }
            return root->val = max(root->val,rightval + leftval);
        }
        else if(leftval != 0 && rightval != 0){
            if(root->left->left != nullptr){
                root->val =  root->val + root->left->left->val;
            }
             if(root->left->right != nullptr){
                root->val = root->val + root->left->right->val;
            }
             if(root->right->left != nullptr){
                root->val =  root->val + root->right->left->val;
            }
             if(root->right->right != nullptr){
                root->val =  root->val + root->right->right->val;
            }
            return root->val = max(root->val,rightval + leftval);
        }
        return root->val;
    }
    int rob(TreeNode* root) {
        if(root->left == nullptr && root->right == nullptr)return root->val;
        return travel(root);
    }
};

121. 买卖股票的最佳时机

给定一个数组 prices ,它的第 i 个元素 prices[i] 表示一支给定股票第 i 天的价格。

你只能选择 某一天 买入这只股票,并选择在 未来的某一个不同的日子 卖出该股票。设计一个算法来计算你所能获取的最大利润。

返回你可以从这笔交易中获取的最大利润。如果你不能获取任何利润,返回 0 。

class Solution {
public:
    int maxProfit(vector<int>& prices) {
        int buy = -1;
        int sell = -1;
        int price = 0;
        for(int i = 1;i < prices.size();i++){
            if(buy == -1 && prices[i] < prices[i - 1]){
                continue;
            }
            else if( prices[i] >= prices[i - 1]){
                if(buy != -1)buy = min(buy,prices[i - 1]);
                else buy = prices[i - 1];
            }
            else if(buy != -1 && prices[i] < prices[i - 1]){
                sell = prices[i - 1];
                price = max(sell - buy,price);
                sell = -1;
            }
        }
        if(buy != -1 && sell == -1){
            price = max(price,prices[prices.size() - 1] - buy);
        }
        return price;
    }
};
class Solution {
public:
//动态规划
    int maxProfit(vector<int>& prices) {
        vector<vector<int>>dp(prices.size(),vector<int>(2,0));
        dp[0][0] = -prices[0];
        dp[0][1] = 0;
        for(int i = 1;i < prices.size();i++){
            dp[i][0] = max(dp[i - 1][0],-prices[i]);
            dp[i][1] = max(dp[i - 1][1],prices[i] + dp[i - 1][0]);
        }
        return dp[prices.size() - 1][1];
    }
};

122.买卖股票的最佳时机II

给你一个整数数组 prices ,其中 prices[i] 表示某支股票第 i 天的价格。

在每一天,你可以决定是否购买和/或出售股票。你在任何时候 最多 只能持有 一股 股票。你也可以先购买,然后在 同一天 出售。

返回 你能获得的 最大 利润

class Solution {
public:
    int maxProfit(vector<int>& prices) {
        int sum = 0;
        int buy = -1;
        int sell = -1;
        for(int i = 1; i < prices.size(); i++){
            if(buy == -1 && prices[i] < prices[i - 1])continue;
            else if(buy != -1 && prices[i] < prices[i - 1]){
                sell = prices[i - 1];
            }
            if(buy == -1){
                buy = prices[i - 1];
                continue;
            }
            if(buy != -1 && sell != -1){
                sum += (sell - buy);
                buy = -1;
                sell = -1;
            }
        }
        if(buy != -1 && sell == -1){
            sum += (prices[prices.size() - 1] - buy);
        }
        return sum;
    }
};
class Solution {
public:
//动态规划
    int maxProfit(vector<int>& prices) {
        vector<vector<int>>dp(prices.size(),vector<int>(2,0));
        dp[0][0] = -prices[0];
        dp[0][1] = 0;
        for(int i = 1;i < prices.size();i++){
            //如果是第i天买入股票,所得现金就是昨天不持有股票的所得现金 减去 今天的股票价格 即:dp[i - 1][1] - prices[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][0] + prices[i]);
        }
        return dp[prices.size() - 1][1];
    }
};

*123.买卖股票的最佳时机III

给定一个数组,它的第 i 个元素是一支给定的股票在第 i 天的价格。

设计一个算法来计算你所能获取的最大利润。你最多可以完成 两笔 交易。

**注意:**你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)。

class Solution {
public:
//前面思路错误提交失败
//看来代码随想录
//创建dp数组
//dp[i][1] 为取第一次购买后最大金额
//dp[i][2] 为第一次最大售卖后金额
//dp[i][3] 为第二次购买后最大金额,将上一次的第一次售卖后金额拿过来买
//dp[i][4] 为第二次售卖后最大金额
    int maxProfit(vector<int>& prices) {
        vector<vector<int>>dp(prices.size(),vector<int>(5,0));
        dp[0][1] = -prices[0];
        dp[0][3] = -prices[0];
        for(int i = 1;i < prices.size();i++){
            dp[i][0] = dp[i - 1][0];
            dp[i][1] = max(dp[i - 1][0] - prices[i],dp[i - 1][1]);
            dp[i][2] = max(dp[i - 1][1] + prices[i],dp[i - 1][2]);
            dp[i][3] = max(dp[i - 1][2] - prices[i],dp[i - 1][3]);
            dp[i][4] = max(dp[i - 1][3] + prices[i],dp[i - 1][4]);
        }
        return dp[prices.size() - 1][4]; 
    }
};

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

给定一个整数数组 prices ,它的第 i 个元素 prices[i] 是一支给定的股票在第 i 天的价格,和一个整型 k

设计一个算法来计算你所能获取的最大利润。你最多可以完成 k 笔交易。也就是说,你最多可以买 k 次,卖 k 次。

**注意:**你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)。

class Solution {
public:
    int maxProfit(int k, vector<int>& prices) {
        vector<vector<int>>dp(prices.size(),vector<int>(k * 2 + 1,0));
        for(int i = 1;i < 2 * k + 1;i += 2){
            dp[0][i] = -prices[0];
        }
        for(int i = 1;i < prices.size();i++){
            dp[i][0] = 0;
            for(int j = 1;j < 2 * k + 1;j++){
                int x = pow(-1,j);
                dp[i][j] = max(dp[i - 1][j],dp[i - 1][j - 1] + x * prices[i]);
            }
        }
        return dp[prices.size()  -1][k * 2];
    }
};

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

给定一个整数数组prices,其中第 prices[i] 表示第 *i* 天的股票价格 。

设计一个算法计算出最大利润。在满足以下约束条件下,你可以尽可能地完成更多的交易(多次买卖一支股票):

  • 卖出股票后,你无法在第二天买入股票 (即冷冻期为 1 天)。

**注意:**你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)。

class Solution {
public:
//状态1:今天买入
//状态2:今天卖出
//状态3:冷冻期
//状态4:前一天卖出状态
    int maxProfit(vector<int>& prices) {
        vector<vector<int>>dp(prices.size(),vector<int>(4,0));
        dp[0][0] = -prices[0];
        for(int i = 1;i < prices.size();i++){
            dp[i][0] = max(dp[i - 1][0],max(dp[i - 1][2] - prices[i],dp[i - 1][3] - prices[i]));
            dp[i][1] = dp[i - 1][0] + prices[i];
            dp[i][2] = dp[i - 1][1];
            dp[i][3] = max(dp[i - 1][3],dp[i - 1][2]);
        }
        return max(dp[prices.size() - 1][1],max(dp[prices.size() - 1][2],dp[prices.size() - 1][3]));
    }
};

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

给定一个整数数组 prices,其中 prices[i]表示第 i 天的股票价格 ;整数 fee 代表了交易股票的手续费用。

你可以无限次地完成交易,但是你每笔交易都需要付手续费。如果你已经购买了一个股票,在卖出它之前你就不能再继续购买股票了。

返回获得利润的最大值。

**注意:**这里的一笔交易指买入持有并卖出股票的整个过程,每笔交易你只需要为支付一次手续费。

class Solution {
public:
    int maxProfit(vector<int>& prices, int fee) {
        vector<vector<int>>dp(prices.size(),vector<int>(2,0));
        dp[0][0] = -prices[0];
        for(int i = 1;i < prices.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][0] + prices[i] - fee);
        }
        return dp[prices.size() - 1][1];
    }
};

300.最长递增子序列

给你一个整数数组 nums ,找到其中最长严格递增子序列的长度。

子序列 是由数组派生而来的序列,删除(或不删除)数组中的元素而不改变其余元素的顺序。例如,[3,6,2,7] 是数组 [0,3,1,6,2,2,7] 的子序列。

class Solution {
public:
    int lengthOfLIS(vector<int>& nums) {
        vector<int>dp(nums.size(),1);
        int mymax = 1;
        for(int i = 1;i < nums.size();i++){
            for(int j = 0;j < i;j++){
                if(nums[i] > nums[j])dp[i] = max(dp[i],dp[j] + 1);
            }
            mymax = max(mymax,dp[i]);
        }
        return mymax;
    }
};

674. 最长连续递增序列

给定一个未经排序的整数数组,找到最长且 连续递增的子序列,并返回该序列的长度。

连续递增的子序列 可以由两个下标 lrl < r)确定,如果对于每个 l <= i < r,都有 nums[i] < nums[i + 1] ,那么子序列 [nums[l], nums[l + 1], ..., nums[r - 1], nums[r]] 就是连续递增子序列。

class Solution {
public:
    int findLengthOfLCIS(vector<int>& nums) {
        int mymax = 1;
        int sum = 1;
        if(nums.size() == 1)return 1;
        for(int i = 1;i < nums.size();i++){
            if(nums[i] > nums[i - 1]){
                sum++;
                mymax = max(mymax,sum); 
            }
            else{
                sum = 1;
            }
        }
        return mymax;
    }
};

*718. 最长重复子数组

给两个整数数组 nums1nums2 ,返回 两个数组中 公共的 、长度最长的子数组的长度

class Solution {
public:
//卡住了
    int findLength(vector<int>& nums1, vector<int>& nums2) {
        int mymax = 0;
        vector<vector<int>>dp(nums1.size() + 1,vector<int>(nums2.size() + 1,0));
        for(int i = 0;i < nums1.size();i++){
            for(int j = 0;j < nums2.size();j++){
                if(nums1[i] == nums2[j])dp[i + 1][j + 1] = dp[i][j] + 1;
                mymax = max(mymax,dp[i + 1][j + 1]);
            }
        }
        return mymax;
    }
};

1143.最长公共子序列

给定两个字符串 text1text2,返回这两个字符串的最长 公共子序列 的长度。如果不存在 公共子序列 ,返回 0

一个字符串的 子序列 是指这样一个新的字符串:它是由原字符串在不改变字符的相对顺序的情况下删除某些字符(也可以不删除任何字符)后组成的新字符串。

  • 例如,"ace""abcde" 的子序列,但 "aec" 不是 "abcde" 的子序列。

两个字符串的 公共子序列 是这两个字符串所共同拥有的子序列。

class Solution {
public:
    int longestCommonSubsequence(string text1, string text2) {
        vector<vector<int>>dp(text1.size() + 1,vector<int>(text2.size() + 1,0));
        for(int i = 0;i < text1.size();i++){
            for(int j = 0;j < text2.size();j++){
                if(text1[i] == text2[j]){
                    dp[i + 1][j + 1] = dp[i][j] + 1; 
                }
                else{
                    dp[i + 1][j + 1] = max(dp[i][j],max(dp[i][j + 1],dp[i + 1][j]));
                }
            }
        }
        return dp[text1.size()][text2.size()];
    }
};

1035.不相交的线

在两条独立的水平线上按给定的顺序写下 nums1nums2 中的整数。

现在,可以绘制一些连接两个数字 nums1[i]nums2[j] 的直线,这些直线需要同时满足满足:

  • nums1[i] == nums2[j]
  • 且绘制的直线不与任何其他连线(非水平线)相交。

请注意,连线即使在端点也不能相交:每个数字只能属于一条连线。

以这种方法绘制线条,并返回可以绘制的最大连线数。

class Solution {
public:
    int maxUncrossedLines(vector<int>& nums1, vector<int>& nums2) {
        vector<vector<int>>dp(nums1.size() + 1,vector<int>(nums2.size() + 1,0));
        for(int i = 0;i < nums1.size();i++){
            for(int j = 0;j < nums2.size();j++){
                if(nums1[i] == nums2[j]){
                    dp[i + 1][j + 1] = dp[i][j] + 1;
                }
                else{
                    dp[i + 1][j + 1] = max(dp[i + 1][j],dp[i][j + 1]);
                }
            }
        }
        return dp[nums1.size()][nums2.size()];
    }
};

53. 最大子序和

给你一个整数数组 nums ,请你找出一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。

子数组 是数组中的一个连续部分。

class Solution {
public:
    int max(int one,int two){
        if(one > two){
            return one;
        }
        else{
            return two;
        }
    }
    int maxSubArray(vector<int>& nums) {
        bool flag = 0;//首先把前面负数排除,其次中间如果累加变成负数也是flag=0
        int sum = nums[0] ,max = sum;
        for(int i = 1;i < nums.size();i++){
            if(sum <= 0 && flag == 0 && nums[i] <= 0){
                sum = this->max(nums[i],sum);
                continue;
            }
            flag = 1;
            if(sum <= 0)sum = 0;
            sum += nums[i];
            max = this->max(sum,max);
            if(sum <= 0){
                flag = 0;
            }
        }
        max = this->max(max,sum);
        return max;
    }
};
class Solution {
public:
    int maxSubArray(vector<int>& nums) {
        vector<int>dp(nums.size() + 1,0);
        int mymax = INT_MIN;
        for(int i = 0;i < nums.size();i++){
            dp[i + 1] = max(nums[i] + dp[i],nums[i]);
            mymax = max(mymax,dp[i + 1]);
        }
        return mymax;
    }
};

392.判断子序列

给定字符串 st ,判断 s 是否为 t 的子序列。

字符串的一个子序列是原始字符串删除一些(也可以不删除)字符而不改变剩余字符相对位置形成的新字符串。(例如,"ace""abcde"的一个子序列,而"aec"不是)。

进阶:

如果有大量输入的 S,称作 S1, S2, … , Sk 其中 k >= 10亿,你需要依次检查它们是否为 T 的子序列。在这种情况下,你会怎样改变代码?

class Solution {
public:
    bool isSubsequence(string s, string t) {
        if(s.size() > t.size())return false;
        vector<vector<int>>dp(s.size() + 1,vector<int>(t.size() + 1,0));
        for(int i = 0;i < s.size();i++){
            for(int j = 0;j < t.size();j++){
                if(s[i] == t[j]){
                    dp[i + 1][j + 1] = dp[i][j] + 1;
                }
                else{
                    dp[i + 1][j + 1] = dp[i + 1][j];
                }
            }
        }
        if(dp[s.size()][t.size()] == s.size())return true;
        return false;
    }
};

*115.不同的子序列

给你两个字符串 st ,统计并返回在 s子序列t 出现的个数。

题目数据保证答案符合 32 位带符号整数范围。

class Solution {
public:
//差临门一脚,还是看了解析
    int numDistinct(string s, string t) {
        if(t.size() > s.size())return 0;
        vector<vector<uint64_t>>dp(s.size() + 1,vector<uint64_t>(t.size() + 1,0));//数据过大,要用long long//long long 也不行要uint64_t
        for(int i = 0;i < s.size();i++)dp[i][0] = 1;
        for(int i = 0;i < s.size();i++){
            for(int j = 0;j < t.size();j++){
                if(s[i] == t[j]){
                    dp[i + 1][j + 1] = dp[i][j] + dp[i][j + 1];//当前这个组合数量由左上方组合数量搭配自己组成数量加上上方原来搭配数量
                }
                else{
                    dp[i + 1][j + 1] = dp[i][j + 1];
                }
            }
        }
        return dp[s.size()][t.size()];
    }
};

583. 两个字符串的删除操作

给定两个单词 word1word2 ,返回使得 word1word2 相同所需的最小步数

每步 可以删除任意一个字符串中的一个字符。

class Solution {
public:
   int minDistance(string word1, string word2) {
       vector<vector<int>>dp(word1.size() + 1,vector<int>(word2.size() + 1,0));
       for(int i = 0;i < word1.size();i++){
           for(int j = 0;j < word2.size();j++){
               if(word1[i] == word2[j]){
                   dp[i + 1][j + 1] = dp[i][j] + 1;
               }
               else{
                   dp[i + 1][j + 1] = max(dp[i + 1][j],dp[i][j + 1]);
               }
           }
       }
       return word1.size() + word2.size() - 2  * dp[word1.size()][word2.size()]; 
   }
};

72. 编辑距离

给你两个单词 word1word2请返回将 word1 转换成 word2 所使用的最少操作数

你可以对一个单词进行如下三种操作:

  • 插入一个字符
  • 删除一个字符
  • 替换一个字符
class Solution {
public:
    int minDistance(string word1, string word2) {
        if(word1.size() == 0)return word2.size();
        if(word2.size() == 0)return word1.size();
        vector<vector<int>>dp(word1.size() + 1,vector<int>(word2.size() + 1,0));
        for(int i = 0;i <= word1.size();i++)dp[i][0] = i;
        for(int j = 0;j <= word2.size();j++)dp[0][j] = j;

        for(int i = 0;i < word1.size();i++){
            for(int j = 0;j < word2.size();j++){
                if(word1[i] == word2[j])dp[i + 1][j + 1] = dp[i][j];
                else dp[i + 1][j + 1] = min(dp[i][j],min(dp[i + 1][j],dp[i][j + 1])) + 1;
            }
        }
        return dp[word1.size()][word2.size()];
    }
};

647. 回文子串

给你一个字符串 s ,请你统计并返回这个字符串中 回文子串 的数目。

回文字符串 是正着读和倒过来读一样的字符串。

子字符串 是字符串中的由连续字符组成的一个序列。

具有不同开始位置或结束位置的子串,即使是由相同的字符组成,也会被视作不同的子串。

class Solution {
public:
    int countSubstrings(string s) {
        vector<string>dp;
        int ret = 0;
        for(int i = 0;i < s.size();i++){
            for(int j = i + 1;j <= s.size();j++){
                string sf = s.substr(i,j - i);       
                dp.push_back(sf);
                reverse(sf.begin(),sf.end());
                if(sf == dp.back()){
                    ret++;
                }
            }
        }
        return ret;
    }
};
class Solution {
public:
    int countSubstrings(string s) {
        vector<vector<int>>dp(s.size(),vector<int>(s.size(),0));
        int ret = 0;
        for(int i = 0;i < s.size();i++){
            for(int j = i;j >= 0;j--){
               if(s[i] == s[j]){
                   if(i - j <= 1){
                       ret++;
                       dp[i][j] = 1;
                   }
                   else if(i - j >1 && dp[i - 1][j + 1]){
                       ret++;
                       dp[i][j] = 1;
                   }
               }
            }
        }
        return ret;
    }
};

516.最长回文子序列

给你一个字符串 s ,找出其中最长的回文子序列,并返回该序列的长度。

子序列定义为:不改变剩余字符顺序的情况下,删除某些字符或者不删除任何字符形成的一个序列。

class Solution {
public:
    int longestPalindromeSubseq(string s) {
        vector<vector<int>>dp(s.size(),vector<int>(s.size(),0));
        for(int i = 0;i < s.size();i++){
            for(int j = i;j >= 0;j--){
                if(s[i] == s[j]){
                    if(i - j <= 1){
                        dp[i][j] = i - j + 1;
                    }
                    else{
                        dp[i][j] = dp[i - 1][j + 1] + 2;
                    }
                }
                else{
                    dp[i][j] = max(dp[i - 1][j],dp[i][j + 1]);
                }
            }
        }
        return dp[s.size() - 1][0];
    }
};

你可能感兴趣的:(C++,刷题,动态规划,算法,c++,leetcode)