算法速成记录贴Day2

......继续。

一.贪心篇(7)

LeetCode376: 摆动序列

思路:设置翻转参数,确定上下or下上流程。

class Solution {
public:
    int wiggleMaxLength(vector<int>& nums) {
        int res = 0;
        int reverse = 0; //初始不知道第一次会上坡还是下坡
        for(int i = 1; i < nums.size(); i++){
            if(nums[i-1]<nums[i] && reverse != 1){
                res++;
                reverse = 1;//记录上坡了
            }
            else if(nums[i-1]>nums[i] && reverse != 2){
                res++;
                reverse = 2;//记录下坡了
            } 
        }
        return res + 1; //res是两两比较得来的值,差一个边界值要+1
    }
};

作者:柯慕灵
链接:https://leetcode.cn/problems/wiggle-subsequence/solutions/2150956/yi-bian-bian-li-qiu-jie-chao-jian-dan-zh-thd7/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

LeetCode738: 单调递增的数字

思路:从左往右遍历各位数字,找到第一个开始下降的数字[i],将[i]减1,然后将[i+1 ...]各位数字全部置为9即可例如:1232123,从左往右遍历,找到第一个开始下降的数字3,将3改为2,然后将后面所有数字全部置为9,最后为:1229999 即为答案

class Solution {
public:
    int monotoneIncreasingDigits(int n) {
        string strn = to_string(n);
        int i = 1;
        while(i<strn.size()&&strn[i-1]<=strn[i])
        {
            i++;
        }
        if(i<strn.size())
        {
            while(i>=2&&strn[i-2]==strn[i-1]) i--;
            strn[i-1] = strn[i-1] - 1;
            for(;i<strn.size();i++) 
            {
                strn[i] = '9';
            }

        }
        return stoi(strn);
    }
};

LeetCode55: 跳跃游戏

思路:如果某一个作为 起跳点 的格子可以跳跃的距离是 3,那么表示后面 3 个格子都可以作为 起跳点 。可以对每一个能作为 起跳点 的格子都尝试跳一次,把 能跳到最远的距离 不断更新。如果可以一直跳到最后,就成功了。

class Solution {
public:
    bool canJump(vector<int>& nums) {
        int k = 0;
        for (int i = 0; i < nums.size(); i++) {
            if (i > k) return false;
            k = max(k, i + nums[i]);
        }
        return true;
    }
};

作者:Ikaruga
链接:https://leetcode.cn/problems/jump-game/solutions/24322/55-by-ikaruga/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

LeetCode45: 跳跃游戏II

每一次跳跃用for循环模拟。

int jump(vector<int> &nums)
{
    int ans = 0;
    int start = 0;
    int end = 1;
    while (end < nums.size())
    {
        int maxPos = 0;
        for (int i = start; i < end; i++)
        {
            // 能跳到最远的距离
            maxPos = max(maxPos, i + nums[i]);
        }
        start = end;      // 下一次起跳点范围开始的格子
        end = maxPos + 1; // 下一次起跳点范围结束的格子
        ans++;            // 跳跃次数
    }
    return ans;
}

作者:Ikaruga
链接:https://leetcode.cn/problems/jump-game-ii/solutions/36035/45-by-ikaruga/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

LeetCode452: 用最少数量的箭引爆气球

思路:考虑所有气球中右边界位置最靠左的那一个,那么一定有一支箭的射出位置就是它的右边界。

class Solution {
public:
    int findMinArrowShots(vector<vector<int>>& points) {
        if (points.empty()) {
            return 0;
        }
        sort(points.begin(), points.end(), [](const vector<int>& u, const vector<int>& v) {
            return u[1] < v[1];
        });
        int pos = points[0][1];
        int ans = 1;
        for (const vector<int>& balloon: points) {
            if (balloon[0] > pos) {
                pos = balloon[1];
                ++ans;
            }
        }
        return ans;
    }
};

作者:力扣官方题解
链接:https://leetcode.cn/problems/minimum-number-of-arrows-to-burst-balloons/solutions/494515/yong-zui-shao-shu-liang-de-jian-yin-bao-qi-qiu-1-2/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

LeetCode56: 合并区间

思路:排序

class Solution {
public:
    vector<vector<int>> merge(vector<vector<int>>& intervals) {
        if (intervals.size() == 0) {
            return {};
        }
        sort(intervals.begin(), intervals.end());
        vector<vector<int>> merged;
        for (int i = 0; i < intervals.size(); ++i) {
            int L = intervals[i][0], R = intervals[i][1];
            if (!merged.size() || merged.back()[1] < L) {
                merged.push_back({L, R});
            }
            else {
                merged.back()[1] = max(merged.back()[1], R);
            }
        }
        return merged;
    }
};

作者:力扣官方题解
链接:https://leetcode.cn/problems/merge-intervals/solutions/203562/he-bing-qu-jian-by-leetcode-solution/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

二.DFS和BFS篇(2)

LeetCode200: 岛屿数量

LeetCode695: 岛屿的最大面积

class Solution {
public:
    int count = 0;
    int numIslands(vector<vector<char>>& grid) {
        for(int i = 0; i < grid.size(); i++) {
            for(int j = 0; j < grid[0].size(); j++) {
                if(grid[i][j] == '1') {
                    count++;
                    dfs(grid, i, j);
                }
            }
        }

        return count;
    }
    
    void dfs(vector<vector<char>>& grid, int r, int c) {
        //判断 base case
        //如果坐标(r,c)超出了网格范围,直接返回
        if(!isArea(grid, r, c)) return;
        
        //如果这个格子不是岛屿,直接返回
        if (grid[r][c] != '1') return;
        
        grid[r][c] = 2; //将格子标记为【已遍历过】
        
        //访问上、下、左、右四个相邻结点
        dfs(grid, r-1, c);
        dfs(grid, r+1, c); 
        dfs(grid, r, c-1); 
        dfs(grid, r, c+1);
    }

    //判断坐标(r,c)是否在网格中
    bool isArea(vector<vector<char>>& grid, int r, int c) {
        return (0 <= r && r < grid.size() && 0 <= c && c < grid[0].size());
    }

};

详细题解参考如下

力扣

三.回溯篇(6)

LeetCode40: 组合总和II

dfs的格式。

  1. i>index的目的就是同一层的相邻平行分支避免数据重复。
  2. 去重需要先排序。
class Solution {
public:

    vector<vector<int>> res;
    vector<int> temp;

    void backtrack(vector<int>& candidates, int target, int index)
    {
        if(target == 0)
        {
            res.push_back(temp);
            return;
        }
        for(int i = index; i < candidates.size() && target-candidates[i] >= 0; i++)
        {
            if(i > index && candidates[i] == candidates[i-1])
                continue;
            temp.push_back(candidates[i]);
            backtrack(candidates, target-candidates[i], i+1);
            temp.pop_back();
        }

    }

    vector<vector<int>> combinationSum2(vector<int>& candidates, int target) {
        sort(candidates.begin(), candidates.end());
        backtrack(candidates, target, 0);
        return res;
    }
};

LeetCode93: 复原IP地址

dfs(给定变量,index=标记处理位置)

  1. 中间的for用来遍历1-3个字符。
  2. substr(开始位置,子串长度)。
class Solution {
public:
    vector<string>ans;
    void backtrace(string& s,int cnt,int index,string& str){
        if(cnt==4 || index==s.size() ){
            if(cnt==4 && index==s.size())
                ans.push_back(str.substr(0,str.size()-1));
            return;
        }
        for(int i=1;i<=3;i++){
            if(index+i>s.size()) return;
            if(s[index]=='0' && i!=1) return;
            if(i==3 && s.substr(index,i)>"255") return;
            str+=s.substr(index,i);
            str.push_back('.');
            backtrace(s,cnt+1,index+i,str);
            str = str.substr(0,str.size()-i-1);
        }
    }
    vector<string> restoreIpAddresses(string s) {
        string str ="";
        backtrace(s,0,0,str);
        return ans;

    }
};

LeetCode90:子集II

不需要其他判断,去重操作参考LeetCode40: 组合总和II

class Solution {
private:
    vector<vector<int>> result;
    vector<int> path;
    void backtracking(vector<int>& nums, int startIndex) {
        result.push_back(path);
        for (int i = startIndex; i < nums.size(); i++) {
            // 而我们要对同一树层使用过的元素进行跳过
            if (i > startIndex && nums[i] == nums[i - 1]) {
                continue;
            }
            path.push_back(nums[i]);
            backtracking(nums, i + 1);
            path.pop_back();
        }
    }

public:
    vector<vector<int>> subsetsWithDup(vector<int>& nums) {
        result.clear();
        path.clear();
        sort(nums.begin(), nums.end()); // 去重需要排序
        backtracking(nums, 0);
        return result;
    }
};

LeetCode491: 递增子序列

此题不能排序去重。

// 版本二
class Solution {
private:
    vector<vector<int>> result;
    vector<int> path;
    void backtracking(vector<int>& nums, int startIndex) {
        if (path.size() > 1) {
            result.push_back(path);
            //不能return 
        }
        int used[201] = {0}; // 这里使用数组来进行去重操作,题目说数值范围[-100, 100]
        for (int i = startIndex; i < nums.size(); i++) {
            if ((!path.empty() && nums[i] < path.back())
                    || used[nums[i] + 100] == 1) {
                    continue;
            }
            used[nums[i] + 100] = 1; // 记录这个元素在本层用过了,本层后面不能再用了
            path.push_back(nums[i]);
            backtracking(nums, i + 1);
            path.pop_back();
        }
    }
public:
    vector<vector<int>> findSubsequences(vector<int>& nums) {
        result.clear();
        path.clear();
        backtracking(nums, 0);
        return result;
    }
};

作者:代码随想录
链接:https://leetcode.cn/problems/non-decreasing-subsequences/solutions/387905/491-di-zeng-zi-xu-lie-shen-sou-hui-su-xiang-jie-by/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

LeetCode47: 全排列II

全排列,每次要从0开始遍历,为了跳过已入栈的元素,需要使用used。

class Solution {
public:
    vector<vector<int>> res;
    vector<int> path;
    map<int, int> hash;

    vector<vector<int>> permuteUnique(vector<int>& nums) {
        sort(nums.begin(), nums.end());
        dfs(0, nums);
        return res;
    }

    void dfs(int index, vector<int>& nums){
        if(index == nums.size()){
            res.push_back(path);
            return;
        }

        for(int i = 0; i < nums.size(); i++){ //i!=startIndex
            if(hash[i] < 1){
                if(i > 0 && nums[i] == nums[i-1] && hash[i-1] == 0) continue;
                path.push_back(nums[i]);
                hash[i]++;
                dfs(index + 1, nums);
                hash[i]--;
                path.pop_back();
            }  
        }
    }

};

 LeetCode494: 目标和

class Solution {
public:
    int count = 0;

    int findTargetSumWays(vector<int>& nums, int target) {
        backtrack(nums, target, 0, 0);
        return count;
    }

    void backtrack(vector<int>& nums, int target, int index, int sum) {
        if (index == nums.size()) {
            if (sum == target) {
                count++;
            }
        } else {
            backtrack(nums, target, index + 1, sum + nums[index]);
            backtrack(nums, target, index + 1, sum - nums[index]);
        }
    }
};

作者:力扣官方题解
链接:https://leetcode.cn/problems/target-sum/solutions/816361/mu-biao-he-by-leetcode-solution-o0cp/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

四.动态规划(16)

重点是找到状态转移方程。

LeetCode53: 最大子序列和

dp[i]:表示以 nums[i] 结尾 的 连续 子数组的最大和。

class Solution {
public:
    int maxSubArray(vector<int>& nums) {
        if(nums.size()==0) return 0;
        if(nums.size()==1) return nums[0];

        vector<int> dp(nums.size(),0);
        dp[0] = nums[0];
        int res = dp[0];
        
        for(int i=1;i<nums.size();i++)
        {
            dp[i] = max(nums[i], dp[i-1]+nums[i]);
            if(dp[i]>res) res=dp[i];
        }

    return res;
    }
};

LeetCode343:整数拆分

dp[i]:表示将正整数 i 拆分成至少两个正整数的和之后,这些正整数的最大乘积

class Solution {
public:
    int integerBreak(int n) {
        vector <int> dp(n + 1);
        for (int i = 2; i <= n; i++) {
            int curMax = 0;
            for (int j = 1; j < i; j++) {
                curMax = max(curMax, max(j * (i - j), j * dp[i - j]));
            }
            dp[i] = curMax;
        }
        return dp[n];
    }
};

作者:力扣官方题解
链接:https://leetcode.cn/problems/integer-break/solutions/352875/zheng-shu-chai-fen-by-leetcode-solution/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

剑指offer 62: 圆圈中最后剩下的数字 == 约瑟夫环

力扣

class Solution {
public:
    int lastRemaining(int n, int m) {
        int x = 0;
        for (int i = 2; i <= n; i++) {
            x = (x + m) % i;
        }
        return x;
    }
};

作者:Krahets
链接:https://leetcode.cn/problems/yuan-quan-zhong-zui-hou-sheng-xia-de-shu-zi-lcof/solutions/607638/jian-zhi-offer-62-yuan-quan-zhong-zui-ho-dcow/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

LeetCode63: 不同路径II

class Solution {
public:
    /**
     * 1. 确定dp数组下标含义 dp[i][j] 从(0,0)到(i,j)可能的路径种类;
     * 2. 递推公式 dp[i][j] = dp[i-1][j] + dp[i][j-1] 但是需要加限制条件就是没有障碍物的时候
     *    if(obstacleGrid[i][j] == 0) dp[i][j] = dp[i-1][j] + dp[i][j-1];
     * 3. 初始化 当obstacleGrid[i][j] == 0时,dp[i][0]=1 dp[0][i]=1 初始化横竖就可;
     * 4. 遍历顺序 一行一行遍历;
     * 5. 推导结果;
     */
    int uniquePathsWithObstacles(vector<vector<int>>& obstacleGrid) {
        /* 计算数组大小 */
        int m = obstacleGrid.size();
        int n = obstacleGrid[0].size();
        /* 定义dp数组 */
        vector<vector<int>> dp(m,vector<int>(n,0));
        /* 初始化dp数组 */
        for(int i = 0; i < m && obstacleGrid[i][0] == 0; i++)
            dp[i][0] = 1; 
        for(int i = 0; i < n && obstacleGrid[0][i] == 0; i++)
            dp[0][i] = 1; 
        /* 一行一行遍历 */
        for(int i = 1; i < m; i++) {
            for(int j = 1; j < n; j++) {
                /* 去除障碍物 */
                if(obstacleGrid[i][j] == 1) continue;
                
                dp[i][j] = dp[i-1][j] + dp[i][j-1];
            }
        }
        return dp[m-1][n-1];
    }
};

滚动数组思想:

节省空间,比如dp[i] = dp[i-1]+未更新的dp[i]

class Solution {
public:
    int uniquePathsWithObstacles(vector<vector<int>>& obstacleGrid) {
        int n = obstacleGrid.size(), m = obstacleGrid.at(0).size();
        vector <int> f(m);

        f[0] = (obstacleGrid[0][0] == 0);
        for (int i = 0; i < n; ++i) {
            for (int j = 0; j < m; ++j) {
                if (obstacleGrid[i][j] == 1) {
                    f[j] = 0;
                    continue;
                }
                if (j - 1 >= 0 && obstacleGrid[i][j - 1] == 0) {
                    f[j] += f[j - 1];
                }
            }
        }

        return f.back();
    }
};

作者:力扣官方题解
链接:https://leetcode.cn/problems/unique-paths-ii/solutions/316968/bu-tong-lu-jing-ii-by-leetcode-solution-2/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

LeetCode64: 最小路径和

class Solution {
public:
    int minPathSum(vector<vector<int>>& grid) {
        if (grid.size() == 0 || grid[0].size() == 0) {
            return 0;
        }
        int rows = grid.size(), columns = grid[0].size();
        auto dp = vector < vector <int> > (rows, vector <int> (columns));
        dp[0][0] = grid[0][0];
        // 初始化
        for (int i = 1;i<rows;i++) {
            dp[i][0] = grid[i][0] + dp[i-1][0];
        }

        for (int i = 1;i<columns;i++) {
            dp[0][i] = grid[0][i] + dp[0][i-1];
        }
        for (int i = 1;i<rows;i++) {
            for (int j=1;j<columns;j++) {
                dp[i][j] = min(dp[i-1][j], dp[i][j-1]) + grid[i][j];
            }
        }
        return dp[rows-1][columns-1];
    }
};

LeetCode416: 分割等和子集

其中 dp[i][j] 表示从数组的 [0,i] 下标范围内选取若干个正整数(可以是 0 个),是否存在一种选取方案使得被选取的正整数的和等于 j。

class Solution {
public:
    bool canPartition(vector<int>& nums) {
        int n = nums.size();
        if (n < 2) {
            return false;
        }
        int sum = accumulate(nums.begin(), nums.end(), 0);
        int maxNum = *max_element(nums.begin(), nums.end());
        if (sum & 1) {           //数组的和必须为偶数
            return false;
        }
        int target = sum / 2;
        if (maxNum > target) {   //最大值不能超过和的1/2
            return false;
        }
        vector<vector<int>> dp(n, vector<int>(target + 1, 0));
        for (int i = 0; i < n; i++) {
            dp[i][0] = true;
        }
        dp[0][nums[0]] = true;
        for (int i = 1; i < n; i++) {
            int num = nums[i];
            for (int j = 1; j <= target; j++) {
                if (j >= num) {
                    dp[i][j] = dp[i - 1][j] | dp[i - 1][j - num];
                } else {
                    dp[i][j] = dp[i - 1][j];
                }
            }
        }
        return dp[n - 1][target];
    }
};

作者:力扣官方题解
链接:https://leetcode.cn/problems/partition-equal-subset-sum/solutions/442320/fen-ge-deng-he-zi-ji-by-leetcode-solution/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

LeetCode322: 零钱兑换

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

作者:力扣官方题解
链接:https://leetcode.cn/problems/coin-change/solutions/132979/322-ling-qian-dui-huan-by-leetcode-solution/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

LeetCode518: 零钱兑换II

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

作者:力扣官方题解
链接:https://leetcode.cn/problems/coin-change-ii/solutions/821278/ling-qian-dui-huan-ii-by-leetcode-soluti-f7uh/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

关于动态规划求组合数/排列数  

LeetCode213: 打家劫舍II

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

private: 
    int myRob(vector<int> nums) {
        int pre = 0, cur = 0, tmp;
        for(int num : nums) {
            tmp = cur;
            cur = max(pre + num, cur);
            pre = tmp;
        }
        return cur;
    }
};

LeetCode714: 买卖股票的最佳时机含手续费 — 这个包含了I II

class Solution {
public:
    int maxProfit(vector<int>& prices, int fee) {
        //dp[i][0]:手不持股票
        //dp[i][1]: 手持股票
        int dp0 = 0, dp1 = -prices[0];
        for( int i = 1; i < prices.size(); ++i ){
            const int tdp0 = dp0, tdp1 = dp1;
            dp0 = max(tdp0, tdp1+prices[i]-fee);//昨天不持,今天无交易;昨天持,今天卖。
            dp1 = max(tdp1, tdp0-prices[i]);//昨天持,今天无交易;昨天不持,今天卖;
        }
        return dp0;
    }
};

LeetCode188: 买卖股票的最佳时机IV — 这个包含了III Hard Pass

LeetCode309: 最佳买卖股票时机含冷冻期

// 思路:
//     考虑有多少种状态,每种状态有哪些选择,或者是做了哪些选择后得到哪种状态。
//     注意:到底是先选择了才有状态,还是先由状态才能选择。这里是先选择了,才有状态

// 状态类型有2种:天数和是否持有。
//     天数:一共为1-n天
//     是否持有:分为持有状态、没持有状态1、没持有状态2。
//         持有状态:选择 无处理 和 买入 都有可能达到该状态
//         没持有状态1:选择 无处理 后达到该状态。
//         没持有状态2:选择 卖出 后达到该状态。注意,卖出后进入一天的冻结期。
//     注意:这里为什么要分两种没持有状态,这是为了便于后续状态转移,如果不区分这两种状态,状态转移没法确定当天是否可以进行买入操作。

// dp表示的含义:
//     dp[i][2] : 第i天为没持有状态2时,此时的最大利润
//     dp[i][1] : 第i天为没持有状态1时,此时的最大利润
//     dp[i][0] : 第i天为持有状态时,此时的最大利润
// 状态转移方程:
//     dp[i][0]: 第i天为持有状态时,此时的最大利润
//         无处理后达到该状态: dp[i][0] = dp[i-1][0] // 第i天没有处理就持有股票,证明上一天也持有
//         买入后达到该状态: dp[i][0] = dp[i-1][1]-prices[n] // 第i天能买入股票,证明上一天没持有股票,且没进行卖出操作
//         所以dp[i][0] = max(dp[i-1][0], dp[i-1][1]-prices[n]); // 这里思考个问题,两种情况都能到达这个状态的话,那如何选择?为什么是取他们的max?
//     dp[i][1]: 第i天为没持有状态1时,此时的最大利润
//         无处理后达到该状态: dp[i][1] = max(dp[i-1][1], dp[i-1][2]) // 有两种到达该状态的情况,取最大那个
//     dp[i][2]: 第i天为没持有状态2时,此时的最大利润
//         卖出后达到该状态: dp[i][2] = dp[i-1][0]+prices[i]

// 最后max(dp[n-1][1], dp[n-1][2])就是题目所需答案。即第n-1天没持有股票时的最大收益

// test case: 
// [1,2,3,0,2]
// [1,2,-2,0,33,0,2]
// [1,2,3,0,2,3,9,0,2,4]
// [2,1]
class Solution {
public:
    int maxProfit(vector<int>& prices) {
        if(prices.size() <= 1)
            return 0;
        int n = prices.size();
        vector<vector<int>> dp(n, vector<int>(3));
        dp[0][0] = -prices[0];
        dp[0][1] = 0;
        dp[0][2] = 0; // 假设默认持有0元股票
        for(int i=1; i<n; ++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[n-1][1], dp[n-1][2]);
    }
};// 思路:
//     考虑有多少种状态,每种状态有哪些选择,或者是做了哪些选择后得到哪种状态。
//     注意:到底是先选择了才有状态,还是先由状态才能选择。这里是先选择了,才有状态

// 状态类型有2种:天数和是否持有。
//     天数:一共为1-n天
//     是否持有:分为持有状态、没持有状态1、没持有状态2。
//         持有状态:选择 无处理 和 买入 都有可能达到该状态
//         没持有状态1:选择 无处理 后达到该状态。
//         没持有状态2:选择 卖出 后达到该状态。注意,卖出后进入一天的冻结期。
//     注意:这里为什么要分两种没持有状态,这是为了便于后续状态转移,如果不区分这两种状态,状态转移没法确定当天是否可以进行买入操作。

// dp表示的含义:
//     dp[i][2] : 第i天为没持有状态2时,此时的最大利润
//     dp[i][1] : 第i天为没持有状态1时,此时的最大利润
//     dp[i][0] : 第i天为持有状态时,此时的最大利润
// 状态转移方程:
//     dp[i][0]: 第i天为持有状态时,此时的最大利润
//         无处理后达到该状态: dp[i][0] = dp[i-1][0] // 第i天没有处理就持有股票,证明上一天也持有
//         买入后达到该状态: dp[i][0] = dp[i-1][1]-prices[n] // 第i天能买入股票,证明上一天没持有股票,且没进行卖出操作
//         所以dp[i][0] = max(dp[i-1][0], dp[i-1][1]-prices[n]); // 这里思考个问题,两种情况都能到达这个状态的话,那如何选择?为什么是取他们的max?
//     dp[i][1]: 第i天为没持有状态1时,此时的最大利润
//         无处理后达到该状态: dp[i][1] = max(dp[i-1][1], dp[i-1][2]) // 有两种到达该状态的情况,取最大那个
//     dp[i][2]: 第i天为没持有状态2时,此时的最大利润
//         卖出后达到该状态: dp[i][2] = dp[i-1][0]+prices[i]

// 最后max(dp[n-1][1], dp[n-1][2])就是题目所需答案。即第n-1天没持有股票时的最大收益

// test case: 
// [1,2,3,0,2]
// [1,2,-2,0,33,0,2]
// [1,2,3,0,2,3,9,0,2,4]
// [2,1]
class Solution {
public:
    int maxProfit(vector<int>& prices) {
        if(prices.size() <= 1)
            return 0;
        int n = prices.size();
        vector<vector<int>> dp(n, vector<int>(3));
        dp[0][0] = -prices[0];
        dp[0][1] = 0;
        dp[0][2] = 0; // 假设默认持有0元股票
        for(int i=1; i<n; ++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[n-1][1], dp[n-1][2]);
    }
};

LeetCode300: 最长递增子序列

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

作者:力扣官方题解
链接:https://leetcode.cn/problems/longest-increasing-subsequence/solutions/147667/zui-chang-shang-sheng-zi-xu-lie-by-leetcode-soluti/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

LeetCode674:最长连续递增子序列

class Solution {
public:
    int findLengthOfLCIS(vector<int>& nums) {
        /* 判断 */
        if(nums.size() <= 1) return nums.size();
        /* 定义记录结果的变量 */
        int result = 0;
        /* 定义dp数组并初始化为1 */
        vector<int> dp(nums.size() + 1, 1);
        /* 遍历 */
        for(int i = 1; i < nums.size(); i++) {
            if(nums[i-1] < nums[i]) {
                /* 不连续递增子序列的跟前0-i 个状态有关,连续递增的子序列只跟前一个状态有关 */
                dp[i] = dp[i-1] + 1;
            }
            /* 记录dp数组中的最大值 */
            if(result < dp[i]) result = dp[i]; 
        }
        return result;
    }
};

作者:Gnakuw
链接:https://leetcode.cn/problems/longest-continuous-increasing-subsequence/solutions/1297309/zui-chang-lian-xu-di-zeng-xu-lie-by-kino-on97/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。


 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值