手撕力扣动态规划:分割等和子集、零钱兑换12、完全平方数、比特位计数、丑数 II、超级丑数、最长回文子串、回文子串、最长重复子数组、字符串相加、36进制加法、编辑距离、 买卖股票的最佳时机34

力扣416. 分割等和子集
只有确定了如下四点,才能把背包问题,套到本题上来。
1.背包的体积为sum / 2
2.背包要放入的商品(集合里的元素)体积为 元素的数值,价值也为元素的数值
3.背包如何正好装满,说明找到了总和为 sum / 2 的子集。
4.背包中每一个元素一定是不可重复放入。
定义里数组为dp[],dp[i] 表示 背包中放入体积为i的商品,最大价值为dp[i]。套到本题,dp[i]表示 背包中总和是i,最大可以凑成总和为i的元素总和为dp[i]。dp[i]一定是小于等于i的,因为背包不能装入超过自身体积的商品(这里理解为元素数值)。如果dp[i] == i 说明,集合中的元素正好可以凑成总和i,理解这一点很重要。

class Solution {
public:
    bool canPartition(vector<int>& nums) {
        int sum = 0;

        // dp[i]中的i表示背包内总和
        // 题目中说:每个数组中的元素不会超过 100,数组的大小不会超过 200
        // 那么背包内总和不会大于20000,所以定义一个20000大的数组。
        vector<int> dp(20001, 0); 
        for (int i = 0; i < nums.size(); i++) {
            sum += nums[i];
        }
        if (sum % 2 == 1) return false;
        int target = sum / 2;

        // 开始 01背包 
        for(int i = 0; i < nums.size(); i++) {
            for(int j = target; j >= nums[i]; j--) { // 每一个元素一定是不可重复放入,所以从大到小遍历
                dp[j] = max(dp[j], dp[j - nums[i]] + nums[i]);
            }
        }
        // 集合中的元素正好可以凑成总和target 
        if (dp[target] == target) return true;
        return false;      
    }
};


作者:carlsun-2
链接:https://leetcode-cn.com/problems/partition-equal-subset-sum/solution/416-fen-ge-deng-he-zi-ji-01bei-bao-de-jing-dian-yi/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

力扣322. 零钱兑换
给定不同面额的硬币 coins 和一个总金额 amount。编写一个函数来计算可以凑成总金额所需的最少的硬币个数。如果没有任何一种硬币组合能组成总金额,返回 -1。
你可以认为每种硬币的数量是无限的。 完全背包

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

力扣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];
    }
};

作者:carlsun-2
链接:https://leetcode-cn.com/problems/coin-change-2/solution/518-ling-qian-dui-huan-iiwan-quan-bei-ba-ynjf/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

力扣279. 完全平方数
给定正整数 n,找到若干个完全平方数(比如 1, 4, 9, 16, …)使得它们的和等于 n。你需要让组成和的完全平方数的个数最少。
思路:
这个题本质上是一个完全背包问题。用完全背包问题的方式描述下本题,就是:
共有 k 个完全平方数,问组成任意一个数最少需要多少个完全平方数,其中每个数字可取无限多个。
是不是如上叙述后就可以很容易理解为是一个完全背包问题。
其中:
背包容量 v[i] — 表示每个完全平方数字的大小
背包价值 w[i] — 为1 。本题求解的是组成数字所需要的完全平方数字的多少,那么价值就是数字个数。
完全背包的模版如下所示:
for (int i = 0; i < k; ++i) {
for (int j = v[i]; j <= n; ++j) {
dp[j] = min(dp[j], dp[j - v[i]] + w[i]);
}
}
初始化条件: 每个数字最多可以由 n 个 1 来表示,因此初始条件为 dp[i] = i
对于本题,我们需要初始化一下每个物品,这里我们需要的是 所有小于给定数字 n 的所有完全平方数。

class Solution {
public:
    int numSquares(int n) {
        vector<int> choices;
        int res = 1;

        // 构造物品,即所有小于给定数字的完全平方数
        while (n / res >= res) {
            choices.push_back(res * res);
            res++;
        }

        const int size = choices.size();
        vector<int> dp(n + 1, 0);

        // init
        for (int i = 0; i <= n; ++i) {
            dp[i] = i;
        }

        // 完全背包模板
        for (int i = 1; i < size; ++i) {
            for (int j = choices[i]; j <= n; ++j) {
                dp[j] = min(dp[j], dp[j - choices[i]] + 1);
            }
        }

        return dp[n];
    }
};

作者:wtffqbpl
链接:https://leetcode-cn.com/problems/perfect-squares/solution/yi-ge-wan-quan-bei-bao-wen-ti-by-wtffqbpl/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

力扣338. 比特位计数
给定一个非负整数 num。对于 0 ≤ i ≤ num 范围中的每个数字 i ,计算其二进制数中的 1 的数目并将它们作为数组返回。
解题思路:
偶数时,比如二进制 1010 其1的个数和它除以2的是一样的,即和 101 带1个数一致
奇数时,加一即可,可以得出
res[i] = res[i >> 1] + (i & 1)

class Solution {
public:
    vector<int> countBits(int num) {
        vector<int> res(num + 1);
        for (int i = 1; i <= num; ++i)
            res[i] = res[i >> 1] + (i & 1);
        return res;
    }
};

作者:zheng-rui-jia
链接:https://leetcode-cn.com/problems/counting-bits/solution/dong-tai-gui-hua-by-zheng-rui-jia-4/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

力扣264. 丑数 II
编写一个程序,找出第 n 个丑数。
丑数就是质因数只包含 2, 3, 5 的正整数。
示例:
输入: n = 10
输出: 12
解释: 1, 2, 3, 4, 5, 6, 8, 9, 10, 12 是前 10 个丑数。

class Solution {
public:
    int nthUglyNumber(int n) {
        vector<int> res(n,0);
        res[0]=1;
        vector<int> index(3,0);
        vector<int> sums={2,3,5};
        for(int i=1;i<n;i++)
        {
            int minTemp=INT_MAX;
            for(int j=0;j<3;j++)
            {
                minTemp=min(minTemp,res[index[j]]*sums[j]);
            }
            for(int j=0;j<3;j++)
            {
                if(res[index[j]]*sums[j]==minTemp)
                index[j]++;
            }
            res[i]=minTemp;
        }
        return res[n-1];
    }
};

作者:yu-yu-yu-bin
链接:https://leetcode-cn.com/problems/ugly-number-ii/solution/dpduo-zhi-zhen-by-yu-yu-yu-bin-fkms/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

力扣313. 超级丑数
编写一段程序来查找第 n 个超级丑数。
超级丑数是指其所有质因数都是长度为 k 的质数列表 primes 中的正整数。
示例:
输入: n = 12, primes = [2,7,13,19]
输出: 32
解释: 给定长度为 4 的质数列表 primes = [2,7,13,19],前 12 个超级丑数序列为:[1,2,4,7,8,13,14,16,19,26,28,32]
思路:
在循环过程中,可以先设置一个变量 mini 保存指针在 ans 中的位置对应的值×自身prime值的最小值,接着再循环一次将所有的指针位置进行更新。

class Solution
{
public:
    int nthSuperUglyNumber(int n, vector<int> &primes)
    {
        int k = primes.size();
        vector<int> ans(n);
        vector<int> kv(k, 0); //用来记录这k个数的指针位置,初始化为0
        ans[0] = 1;
        for (int i = 1; i < n; i++)
        {
            int mini = INT_MAX;
            for (int j = 0; j < k; j++)
            {
                mini = min(mini, ans[kv[j]] * primes[j]);
            }
            for (int j = 0; j < k; j++)
            {
                if (mini == ans[kv[j]] * primes[j])
                {
                    kv[j]++;
                }
            }
            ans[i] = mini; //记录下最小值
        }
        return ans[n - 1];
    }
};

作者:xian-yu-zhi-wang
链接:https://leetcode-cn.com/problems/super-ugly-number/solution/leetcode-313chao-ji-chou-shu-ti-jie-by-hongyang-ya/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

力扣005. 最长回文子串
给定一个字符串 s,找到 s 中最长的回文子串。你可以假设 s 的最大长度为 1000。
动态规划:
在这里插入图片描述

class Solution {
public:
    string longestPalindrome(string s) {
        int n = s.size();
        vector<vector<int>> dp(n, vector<int>(n));
        string ans;
        for (int l = 0; l < n; ++l) {
            for (int i = 0; i + l < n; ++i) {
                int j = i + l;
                if (l == 0) {
                    dp[i][j] = 1;
                } else if (l == 1) {
                    dp[i][j] = (s[i] == s[j]);
                } else {
                    dp[i][j] = (s[i] == s[j] && dp[i + 1][j - 1]);
                }
                if (dp[i][j] && l + 1 > ans.size()) {
                    ans = s.substr(i, l + 1);
                }
            }
        }
        return ans;
    }
};

作者:LeetCode-Solution
链接:https://leetcode-cn.com/problems/longest-palindromic-substring/solution/zui-chang-hui-wen-zi-chuan-by-leetcode-solution/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

中心扩散法:

class Solution {
public:
    string longestPalindrome(string s) {
        int len=s.size();
        if(len==0||len==1)
            return s;
        int start=0;//记录回文子串起始位置
        int end=0;//记录回文子串终止位置
        int mlen=0;//记录最大回文子串的长度
        for(int i=0;i<len;i++)
        {
            int len1=expendaroundcenter(s,i,i);//一个元素为中心
            int len2=expendaroundcenter(s,i,i+1);//两个元素为中心
            mlen=max(max(len1,len2),mlen);
            if(mlen>end-start+1)
            {
                start=i-(mlen-1)/2;
                end=i+mlen/2;
            }
        }
        return s.substr(start,mlen);
        //该函数的意思是获取从start开始长度为mlen长度的字符串
    }
private:
    int expendaroundcenter(string s,int left,int right)
    //计算以left和right为中心的回文串长度
    {
        int L=left;
        int R=right;
        while(L>=0 && R<s.length() && s[R]==s[L])
        {
            L--;
            R++;
        }
        return R-L-1;
    }
};

作者:chenlele
链接:https://leetcode.cn/problems/longest-palindromic-substring/solution/zui-chang-hui-wen-zi-chuan-c-by-gpe3dbjds1/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

力扣647. 回文子串
给定一个字符串,你的任务是计算这个字符串中有多少个回文子串。
具有不同开始位置或结束位置的子串,即使是由相同的字符组成,也会被视作不同的子串。
动态规划:

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

中心扩散:

class Solution {
public:
    int countSubstrings(string s) {
        int num = 0;
        int n = s.size(); 
        for(int i=0;i<n;i++)//遍历回文中心点
        {
            for(int j=0;j<=1;j++)//j=0,中心是一个点,j=1,中心是两个点
            {
                int l = i;
                int r = i+j;
                while(l>=0 && r<n && s[l--]==s[r++])num++;
            }
        }
        return num;
    }
};

力扣718. 最长重复子数组
给两个整数数组 A 和 B ,返回两个数组中公共的、长度最长的子数组的长度。
输入:
A: [1,2,3,2,1]
B: [3,2,1,4,7]
输出:3
解释:
长度最长的公共子数组是 [3, 2, 1] 。
思路:
dp[i][j]表示以A数组第i个元素和B数组第j个元素为结尾的公共子数组的最大长度
if(A[i-1]!=B[i-1]): dp[i][j]=0;
else: dp[i][j]=dp[i-1][j-1]+1;

class Solution {
public:
    int findLength(vector<int>& A, vector<int>& B) {
        int m=A.size();int n=B.size();
        int ans=0;
        vector<vector<int>> dp(m+1,vector<int>(n+1,0));
        for(int i=1;i<=m;i++)
        {
            for(int j=1;j<=n;j++)
            {
                if(A[i-1]==B[j-1])
                  {
                  dp[i][j]=dp[i-1][j-1]+1;
                  ans=max(ans,dp[i][j]);
                  }
                else
                  dp[i][j]=0;
            }
        }
        return ans;
    }
};

滑动窗口解法
在这里插入图片描述

class Solution {
public:
    int findLength(vector<int>& A, vector<int>& B) {
        return A.size()<=B.size()?helper(A,B):helper(B,A);
    }
        //窗口滑动的重叠过程可以分为三个过程:
        //1、下面子串进入,开始重叠的过程
        //2、上下两子串完全重叠,中间过程
        //3、下面的子串开始离开,重叠区域减少,离开过程
    int helper(vector<int>& A,vector<int>& B){
        int na=A.size();
        int nb=B.size();
        int ret=0;
        //进入时候的处理
        for(int len=1;len<=na;len++){
            int tmplen=maxlen(A,0,B,nb-len,len);
            ret=max(ret,tmplen);
        }
        //中间过程的处理
        for(int indexb=nb;indexb-na>=0;indexb--){
            int tmplen=maxlen(A,0,B,indexb-na,na);
            ret=max(ret,tmplen);
        }
        //出去时的处理
        for(int len=na;len>0;len--){
            int tmplen=maxlen(A,na-len,B,0,len);
            ret=max(ret,tmplen);
        }
        return ret;
    }
    int maxlen(vector<int>& A,int indexa,vector<int>& B,int indexb,int len){
        int ret=0;
        int countnum=0;
        for(int i=0;i<len;i++){
            if(A[indexa+i]==B[indexb+i]){
                countnum++;
            }
            else if(countnum>0){
                ret=max(ret,countnum);
                countnum=0;
            }
        }
        return max(ret,countnum);
    }
};

力扣415. 字符串相加
给定两个字符串形式的非负整数 num1 和num2 ,计算它们的和。
提示:
num1 和num2 的长度都小于 5100
num1 和num2 都只包含数字 0-9
num1 和num2 都不包含任何前导零
你不能使用任何內建 BigInteger 库, 也不能直接将输入的字符串转换为整数形式

class Solution {
public:
    string addStrings(string num1, string num2) {
        string str;
        int cur = 0, i = num1.size()-1, j = num2.size()-1;
        while (i >= 0 || j >= 0 || cur != 0) {
            if (i >= 0) cur += num1[i--] - '0';
            if (j >= 0) cur += num2[j--] - '0';
            str += to_string (cur % 10);
            cur /= 10;
        }
        reverse(str.begin(), str.end());
        return str;
    }
};

牛客36进制加法
36进制数由 0~9 , a~z 共 36 个字符组成,请你按照加法规则计算两个 36 进制数的和。
例如 1b+2b = 3m
两个数中包含的字符仅包括 0 - 9和 a - z

class Solution {
public:
    /**
     * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
     *
     * 
     * @param A string字符串 
     * @param B string字符串 
     * @return string字符串
     */
    int getInt(char c) {
        if(c <= '9' && c >= '0') {
            return c - '0';
        }
        else {
            return c - 'a' + 10;
        }
    }
    
    char getChar(int num) {
        if(num <= 9) {
            return num + '0';
        }
        else {
            return num - 10 + 'a';
        }
    }
    string thirtysixAdd(string A, string B) {
        // write code here
        string  res;
        int i = A.size() - 1;
        int j = B.size() - 1;
        int carry = 0;
        while(i >= 0 || j >= 0 || carry) {
            if(i >= 0) {
                carry += getInt(A[i--]);
            }
            if(j >= 0) {
                carry += getInt(B[j--]);
            }
            res += getChar(carry % 36);
            carry /= 36;
        }
        reverse(res.begin(), res.end());
        return res;
    }
};

力扣072. 编辑距离
力扣题目:
给你两个单词 word1 和 word2,请你计算出将 word1 转换成 word2 所使用的最少操作数 。
你可以对一个单词进行如下三种操作:

插入一个字符
删除一个字符
替换一个字符

啥叫编辑距离?我们说word1和word2的编辑距离为X,意味着word1经过X步,变成了word2,咋变的你不用管,反正知道就需要X步,并且这是个最少的步数。

我们有word1和word2,我们定义dp[i][j]的含义为:word1的前i个字符和word2的前j个字符的编辑距离。意思就是word1的前i个字符,变成word2的前j个字符,最少需要这么多步。

例如word1 = “horse”, word2 = “ros”,那么dp[3][2]=X就表示"hor"和“ro”的编辑距离,即把"hor"变成“ro”最少需要X步。

如果下标为零则表示空串,比如:dp[0][2]就表示空串""和“ro”的编辑距离

定理一:如果其中一个字符串是空串,那么编辑距离是另一个字符串的长度。比如空串“”和“ro”的编辑距离是2(做两次“插入”操作)。再比如"hor"和空串“”的编辑距离是3(做三次“删除”操作)。

定理二:当i>0,j>0时(即两个串都不空时)dp[i][j]=min(dp[i-1][j]+1,dp[i][j-1]+1,dp[i-1][j-1]+int(word1[i]!=word2[j]))。

啥意思呢?举个例子,word1 = “abcde”, word2 = “fgh”,我们现在算这俩字符串的编辑距离,就是找从word1,最少多少步,能变成word2?那就有三种方式:

知道"abcd"变成"fgh"多少步(假设X步),那么从"abcde"到"fgh"就是"abcde"->"abcd"->"fgh"。(一次删除,加X步,总共X+1步)
知道"abcde"变成“fg”多少步(假设Y步),那么从"abcde"到"fgh"就是"abcde"->"fg"->"fgh"。(先Y步,再一次添加,加X步,总共Y+1步)
知道"abcd"变成“fg”多少步(假设Z步),那么从"abcde"到"fgh"就是"abcde"->"fge"->"fgh"。(先不管最后一个字符,把前面的先变好,用了Z步,然后把最后一个字符给替换了。这里如果最后一个字符碰巧就一样,那就不用替换,省了一步)

以上三种方式算出来选最少的,就是答案。所以我们再看看定理二:

dp[i][j]=min(dp[i-1][j]+1,dp[i][j+1]+1,dp[i][j]+int(word1[i]!=word2[j]))
dp[i-1][j]:情况一
dp[i][j-1]+1:情况二
dp[i-1][j-1]+int(word1[i]!=word2[j]):情况三

有了定理二的递推公式,你就建立一个二维数组,考虑好空串的情况,总会写出来

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<dp.size();i++)
        {
            dp[i][0]=i;
        }
        for(int j=0;j<dp[0].size();j++)
        {
            dp[0][j]=j;
        }
        for(int i=1;i<dp.size();i++)
        {
            for(int j=1;j<dp[i].size();j++)
            {
                if(word1[i-1]!=word2[j-1])
                {
                   dp[i][j]=min(min(dp[i-1][j],dp[i][j-1]),dp[i-1][j-1])+1;
                }
                else
                dp[i][j]=dp[i-1][j-1];
            }
        }
        return dp[word1.size()][word2.size()];
    }
};

牛客最小编辑代价
给定两个字符串str1和str2,再给定三个整数ic,dc和rc,分别代表插入、删除和替换一个字符的代价,请输出将str1编辑成str2的最小代价。
思路:
dp[i][j]: 将str1(0-i)编辑成str2(0-j)的最小代价。
dp[i][j] = min(dp[i-1][j] + dc, dp[i][j-1] + ic) : 删掉str1[i]或者插入str2[j](不管str1[i] 与str2[j]是否相等,都可以这样做)
dp[i][j] = min(dp[i][j], dp[i-1][j-1]) str1[i] = str2[j]
dp[i][j] = min(dp[i][j], dp[i-1][j-1]+ rc) str1[i] != str2[j]

摘自力扣评论:
对“dp[i-1][j-1] 表示替换操作,dp[i-1][j] 表示删除操作,dp[i][j-1] 表示插入操作。”的补充理解:

以 word1 为 “horse”,word2 为 “ros”,且 dp[5][3] 为例,即要将 word1的前 5 个字符转换为 word2的前 3 个字符,也就是将 horse 转换为 ros,因此有:

(1) dp[i-1][j-1],即先将 word1 的前 4 个字符 hors 转换为 word2 的前 2 个字符 ro,然后将第五个字符 word1[4](因为下标基数以 0 开始) 由 e 替换为 s(即替换为 word2 的第三个字符,word2[2])

(2) dp[i][j-1],即先将 word1 的前 5 个字符 horse 转换为 word2 的前 2 个字符 ro,然后在末尾补充一个 s,即插入操作

(3) dp[i-1][j],即先将 word1 的前 4 个字符 hors 转换为 word2 的前 3 个字符 ros,然后删除 word1 的第 5 个字符

class Solution {
public:
    int minEditCost(string str1, string str2, int ic, int dc, int rc) {
        if(str1==""&&str2=="") return 0;
        int len1 = str1.size();
        int len2 = str2.size();
        //想清楚状态矩阵的定义,下标代表长度!!
        int dp[len1+1][len2+1];
        for(int i=0;i<=len1;i++){
            dp[i][0] = i*dc;//str1所有字符全部删除变成str2
        }
        for(int j=0;j<=len2;j++){
            dp[0][j] = j*ic;//空字符串str1全部插入变成str2
        }
        for(int i=1;i<=len1;i++){
            for(int j=1;j<=len2;j++){
                if(str1[i-1]==str2[j-1]) dp[i][j] = dp[i-1][j-1];
                else{
                    //等价于str1的前i-1个字符和str2的前j-1个字符编辑距离的子问题。
                    //即对于str1的第i个字符'X',对于str2的第j个字符'W',我们在str1的末尾'X'替换为字符'W',
                    //那么dp[i][j]最小可以为dp[i-1][j-1]+rc;
                    int choice1 = dp[i-1][j-1] + rc;//替换
                    
                    //等价于str1的前i个字符和str2的前j-1个字符编辑距离的子问题。
                    //即对于str2的第j个字符'X',我们在str1的末尾添加了一个相同的字符'X',
                    //那么dp[i][j]最小可以为dp[i][j-1]+ic;
                    int choice2 = dp[i][j-1]+ic;//插入
                    
                    //等价于str1的前i-1个字符和str2的前j个字符编辑距离的子问题。
                    //即对于str1的第i个字符'X',我们在str2的末尾添加了一个相同的字符'X',等价于在str1的末尾删除了该字符'X',
                    //那么dp[i][j]最小可以为dp[i][j-1]+dc;
                    int choice3 = dp[i-1][j]+dc;//删除
                    dp[i][j] = min(choice1,min(choice2,choice3));
                }
            }
        }
        return dp[len1][len2];
    }
};

力扣123. 买卖股票的最佳时机 III
给定一个数组,它的第 i 个元素是一支给定的股票在第 i 天的价格。
设计一个算法来计算你所能获取的最大利润。你最多可以完成 两笔 交易。
注意:你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)。

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

作者:carlsun-2
链接:https://leetcode-cn.com/problems/best-time-to-buy-and-sell-stock-iii/solution/123-mai-mai-gu-piao-de-zui-jia-shi-ji-ii-zfh9/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

力扣188. 买卖股票的最佳时机 IV
给定一个整数数组 prices ,它的第 i 个元素 prices[i] 是一支给定的股票在第 i 天的价格。
设计一个算法来计算你所能获取的最大利润。你最多可以完成 k 笔交易。
注意:你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)。
在这里插入图片描述

class Solution {
public:
    int maxProfit(int k, vector<int>& prices) {

        if (prices.size() == 0) return 0;
        vector<vector<int>> dp(prices.size(), vector<int>(2 * k + 1, 0));
        for (int j = 1; j < 2 * k; j += 2) {
            dp[0][j] = -prices[0];
        }
        for (int i = 1;i < prices.size(); i++) {
            for (int j = 0; j < 2 * k - 1; j += 2) { 
                dp[i][j + 1] = max(dp[i - 1][j + 1], dp[i - 1][j] - prices[i]);
                dp[i][j + 2] = max(dp[i - 1][j + 2], dp[i - 1][j + 1] + prices[i]);
            }
        }
        return dp[prices.size() - 1][2 * k];
    }
};

作者:carlsun-2
链接:https://leetcode-cn.com/problems/best-time-to-buy-and-sell-stock-iv/solution/188-mai-mai-gu-piao-de-zui-jia-shi-ji-iv-qgcl/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值