前端常见算法题(动态规划篇)--上

376 阅读5分钟

一、路径问题

2021.05.13

No.514 自由之路

电子游戏“辐射4”中,任务“通向自由”要求玩家到达名为“Freedom Trail Ring”的金属表盘,并使用表盘拼写特定关键词才能开门。​ 给定一个字符串 ring,表示刻在外环上的编码;给定另一个字符串 key,表示需要拼写的关键词。您需要算出能够拼写关键词中所有字符的最少步数。 ​

最初,ring 的第一个字符与12:00方向对齐。您需要顺时针或逆时针旋转 ring 以使 key 的一个字符在 12:00 方向对齐,然后按下中心按钮,以此逐个拼写完 key 中的所有字符。 ​

旋转 ring 拼出 key 字符 key[i] 的阶段中: ​

您可以将 ring 顺时针或逆时针旋转一个位置,计为1步。旋转的最终目的是将字符串 ring 的一个字符与 12:00 方向对齐,并且这个字符必须等于字符 key[i] 。 如果字符 key[i] 已经对齐到12:00方向,您需要按下中心按钮进行拼写,这也将算作 1 步。按完之后,您可以开始拼写 key 的下一个字符(下一阶段), 直至完成所有拼写。 示例: ​

 ring.jpg

  输入: ring = "godding", key = "gd" 输出: 4 解释: 对于 key 的第一个字符 'g',已经在正确的位置, 我们只需要1步来拼写这个字符。 对于 key 的第二个字符 'd',我们需要逆时针旋转 ring "godding" 2步使它变成 "ddinggo"。 当然, 我们还需要1步进行拼写。 因此最终的输出是 4。 提示: ​

ring 和 key 的字符串长度取值范围均为 1 至 100; 两个字符串中都只有小写字符,并且均可能存在重复字符; 字符串 key 一定可以由字符串 ring 旋转拼出。 ​

来源:力扣(LeetCode) 链接:leetcode-cn.com/problems/fr… 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

方案:

/*
 * @lc app=leetcode.cn id=514 lang=javascript
 *
 * [514] 自由之路
 */

// @lc code=start
/**
 * @param {string} ring
 * @param {string} key
 * @return {number}
 */
var findRotateSteps = function(ring, key) {
    // 用于存储ring中的索引信息
    const keyMap = {};

    for(let i = 0; i < ring.length; i++) {
        const k = ring[i];
        if(keyMap[k]) {
            keyMap[k].push(i)
        } else {
            keyMap[k] = [i]
        }
    }

    // 缓存用于dfs剪枝
    const memo = new Array(ring.length);

    for(let i = 0; i < ring.length; i++) {
        memo[i] = new Array(key.length).fill(-1)
    }
    
    // dfs递归
    const dfs = ( ringI, keyI ) => {
        if(keyI == key.length) return 0;

        // 剪枝 有缓存直接返回缓存的结果
        if( memo[ringI][keyI] !== -1 ) return memo[ringI][keyI]

        const cur = key[keyI];

        // 返回的结果
        let res = Infinity;
        for(const targetI of keyMap[cur]) {
            // 正向位置
            let d1 = Math.abs(ringI - targetI),
                d2 = ring.length - d1;
            const curMin = Math.min(d1, d2)
            // 递归的循环不变式
            res = Math.min(res, curMin + dfs(targetI, keyI+1))
        }
        memo[ringI][keyI] = res;
        return res;
    }

    return dfs(0,0) + key.length;

};

动态规划,关键在于找到剪枝优化方案



2021.05.16

No.576 出界的路径数

给定一个 m × n 的网格和一个球。球的起始坐标为 (i,j) ,你可以将球移到相邻的单元格内,或者往上、下、左、右四个方向上移动使球穿过网格边界。但是,你最多可以移动 N 次。找出可以将球移出边界的路径数量。答案可能非常大,返回 结果 mod 109 + 7 的值。​   ​

示例 1: ​

输入: m = 2, n = 2, N = 2, i = 0, j = 0 输出: 6 解释: ​

out_of_boundary_paths_1.png

示例 2: ​

输入: m = 1, n = 3, N = 3, i = 0, j = 1 输出: 12 解释: ​

 out_of_boundary_paths_2.png

说明: ​

球一旦出界,就不能再被移动回网格内。 网格的长度和高度在 [1,50] 的范围内。 N 在 [0,50] 的范围内。 ​

来源:力扣(LeetCode) 链接:leetcode-cn.com/problems/ou… 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

方案:

/*
 * @lc app=leetcode.cn id=576 lang=javascript
 *
 * [576] 出界的路径数
 */

// @lc code=start
/**
 * @param {number} m
 * @param {number} n
 * @param {number} maxMove
 * @param {number} startRow
 * @param {number} startColumn
 * @return {number}
 */
var findPaths = function(m, n, N, i, j) {
    const helper = (i, j, N) => {
        // N 次用完了,此时不能再走了就返回 0
        if (N < 0) {
            return 0
        }
        // 出界条件满足了,说明有一条出界路径,就返回 1
        if (i < 0 || i >= m || j < 0 || j >= n) {
            return 1
        }
        // 记忆化搜索(如果重复访问了那就用之前的值)
        const key = `${i}-${j}-${N}`
        if (visited.has(key)) {
            return visited.get(key)
        }
        let res = 0
        // 找上、下、左、右 四个方向
        for (let k = 0; k < 4; k++) {
            res = (res + helper(i + direction[k][0], j + direction[k][1], N -1)) % mod
        }
        // 将当前的值缓存下来
        visited.set(key, res)
        return res
    }
    const mod = Math.pow(10, 9) + 7
    const direction = [[1, 0], [-1, 0], [0, -1], [0, 1]]
    const visited = new Map()
    return helper(i, j, N)
};

利用Map进行递归判断,状态转移是四个方向的探索



2021.05.17

No.980 不同路径-iii

在二维网格 grid 上,有 4 种类型的方格:​ 1 表示起始方格。且只有一个起始方格。 2 表示结束方格,且只有一个结束方格。 0 表示我们可以走过的空方格。 -1 表示我们无法跨越的障碍。 返回在四个方向(上、下、左、右)上行走时,从起始方格到结束方格的不同路径的数目。 ​

每一个无障碍方格都要通过一次,但是一条路径中不能重复通过同一个方格。 ​

  ​

示例 1: ​

输入:[[1,0,0,0],[0,0,0,0],[0,0,2,-1]] 输出:2 解释:我们有以下两条路径:

  1. (0,0),(0,1),(0,2),(0,3),(1,3),(1,2),(1,1),(1,0),(2,0),(2,1),(2,2)
  2. (0,0),(1,0),(2,0),(2,1),(1,1),(0,1),(0,2),(0,3),(1,3),(1,2),(2,2) 示例 2: ​

输入:[[1,0,0,0],[0,0,0,0],[0,0,0,2]] 输出:4 解释:我们有以下四条路径:

  1. (0,0),(0,1),(0,2),(0,3),(1,3),(1,2),(1,1),(1,0),(2,0),(2,1),(2,2),(2,3)
  2. (0,0),(0,1),(1,1),(1,0),(2,0),(2,1),(2,2),(1,2),(0,2),(0,3),(1,3),(2,3)
  3. (0,0),(1,0),(2,0),(2,1),(2,2),(1,2),(1,1),(0,1),(0,2),(0,3),(1,3),(2,3)
  4. (0,0),(1,0),(2,0),(2,1),(1,1),(0,1),(0,2),(0,3),(1,3),(1,2),(2,2),(2,3) 示例 3: ​

输入:[[0,1],[2,0]] 输出:0 解释: 没有一条路能完全穿过每一个空的方格一次。 请注意,起始和结束方格可以位于网格中的任意位置。   ​

提示: ​

1 <= grid.length * grid[0].length <= 20 ​

来源:力扣(LeetCode) 链接:leetcode-cn.com/problems/un… 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

方案:

/*
 * @lc app=leetcode.cn id=980 lang=javascript
 *
 * [980] 不同路径 III
 */

// @lc code=start
/**
 * @param {number[][]} grid
 * @return {number}
 */
var uniquePathsIII = function(grid) {
    if(!grid.length) return 0;

    const helper = ( grid, i, j, step ) => {
        if( i < 0 || i >= grid.length || j < 0 || j >= grid[0].length || grid[i][j] == -1 ) {
            return 0;
        }

        if(grid[i][j] == 2) return step == -1 ? 1 : 0;

        grid[i][j] = -1;

        let res = 0;

        // 向四个方向探索
        res += helper( grid, i+1, j, step - 1 );
        res += helper( grid, i, j+1, step - 1 );
        res += helper( grid, i-1, j, step - 1 );
        res += helper( grid, i, j-1, step - 1 );

        grid[i][j] = 0

        return res;
    }

    let startI = 0, startJ = 0, total = 0;

    // 遍历
    for( let i = 0; i < grid.length; i++ ) {
        for( let j = 0; j < grid[i].length; j++ ) {
            if( grid[i][j] == 1 ) {
                startI = i;
                startJ = j;
            }

            if( grid[i][j] == 0 ) {
                total++;
            }
        }
    }

    return helper(grid, startI, startJ, total);
};


同576题,不同之处在于不能返回,动态规划进行四个方向的探索回溯



2021.05.18

No.1129 颜色交替的最短路径

在一个有向图中,节点分别标记为 0, 1, ..., n-1。这个图中的每条边不是红色就是蓝色,且存在自环或平行边。​ red_edges 中的每一个 [i, j] 对表示从节点 i 到节点 j 的红色有向边。类似地,blue_edges 中的每一个 [i, j] 对表示从节点 i 到节点 j 的蓝色有向边。 ​

返回长度为 n 的数组 answer,其中 answer[X] 是从节点 0 到节点 X 的红色边和蓝色边交替出现的最短路径的长度。如果不存在这样的路径,那么 answer[x] = -1。 ​

  ​

示例 1: ​

输入:n = 3, red_edges = [[0,1],[1,2]], blue_edges = [] 输出:[0,1,-1] 示例 2: ​

输入:n = 3, red_edges = [[0,1]], blue_edges = [[2,1]] 输出:[0,1,-1] 示例 3: ​

输入:n = 3, red_edges = [[1,0]], blue_edges = [[2,1]] 输出:[0,-1,-1] 示例 4: ​

输入:n = 3, red_edges = [[0,1]], blue_edges = [[1,2]] 输出:[0,1,2] 示例 5: ​

输入:n = 3, red_edges = [[0,1],[0,2]], blue_edges = [[1,0]] 输出:[0,1,1]   ​

提示: ​

1 <= n <= 100 red_edges.length <= 400 blue_edges.length <= 400 red_edges[i].length == blue_edges[i].length == 2 0 <= red_edges[i][j], blue_edges[i][j] < n ​

来源:力扣(LeetCode) 链接:leetcode-cn.com/problems/sh… 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

方案:

/*
 * @lc app=leetcode.cn id=1129 lang=javascript
 *
 * [1129] 颜色交替的最短路径
 */

// @lc code=start
/**
 * @param {number} n
 * @param {number[][]} red_edges
 * @param {number[][]} blue_edges
 * @return {number[]}
 */
var shortestAlternatingPaths = function(n, red_edges, blue_edges) {
    var re_0=new Array(n).fill(Number.MAX_VALUE);
    var re_1=new Array(n).fill(Number.MAX_VALUE);

   
    var graph_red=new Array(n);
    var graph_blue=new Array(n);
    for(var i=0;i<n;i++){
        graph_red[i]= new Array();
        graph_blue[i]=new Array();

    }
    for(var i=0;i<red_edges.length;i++){
        graph_red[red_edges[i][0]].push(red_edges[i][1]);
    }
    for(var i=0;i<blue_edges.length;i++){
        graph_blue[blue_edges[i][0]].push(blue_edges[i][1]);
    }

    re_1[0]=0;
    re_0[0]=0;
    var now_b=[0],now_r=[0];
    step=0;
    while(now_b.length!==0 ||now_r.length!==0){
        var new_b=[], new_r=[];
        var point,adj;
        step++;
        while(now_b.length!==0){
            point=now_b.pop();
            adj=graph_red[point];
            for(var next of adj){
                if(re_0[next]===Number.MAX_VALUE){
                    re_0[next]=step;
                    new_r.push(next);
                }
            }
        }
        while(now_r.length!=0){
            point=now_r.pop();
            adj=graph_blue[point];
            for(var next of adj){
                if(re_1[next]===Number.MAX_VALUE){
                    re_1[next]=step;
                    new_b.push(next);
                }
            }
        }
        now_r=new_r;
        now_b=new_b;
    }
    //console.log(re_0,re_1);
    for(var i=0;i<n;i++){
        re_0[i]=Math.min(re_0[i],re_1[i]);
        if(re_0[i]===Number.MAX_VALUE) re_0[i]=-1;
    }

    return re_0;

};


Dijistra算法变形,使用动态规划进行逐步bfs



总结:

  1. 路径问题最常见的就是回溯探索,关键在于剪枝优化,对于状态转移函数的归纳总结;
  2. 动态规划是一种逐步处理问题的思路,常见的需要利用二维数组及hashMap等数据结构进行处理

二、股票问题

2021.05.19

No.121 买卖股票的最佳时机

给定一个数组 prices ,它的第 i 个元素 prices[i] 表示一支给定股票第 i 天的价格。​ 你只能选择 某一天 买入这只股票,并选择在 未来的某一个不同的日子 卖出该股票。设计一个算法来计算你所能获取的最大利润。 ​

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

  ​

示例 1: ​

输入:[7,1,5,3,6,4] 输出:5 解释:在第 2 天(股票价格 = 1)的时候买入,在第 5 天(股票价格 = 6)的时候卖出,最大利润 = 6-1 = 5 。 注意利润不能是 7-1 = 6, 因为卖出价格需要大于买入价格;同时,你不能在买入前卖出股票。 示例 2: ​

输入:prices = [7,6,4,3,1] 输出:0 解释:在这种情况下, 没有交易完成, 所以最大利润为 0。   ​

提示: ​

1 <= prices.length <= 105 0 <= prices[i] <= 104 ​

来源:力扣(LeetCode) 链接:leetcode-cn.com/problems/be… 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

方案:

/*
 * @lc app=leetcode.cn id=3 lang=javascript
 *
 * [3] 无重复字符的最长子串
 */

// @lc code=start
/**
 * @param {string} s
 * @return {number}
 */
var lengthOfLongestSubstring = function(s) {
    let max = 0, index = 0;

    for(let i=0,j=0;j<s.length;j++) {
        index = s.slice(i,j).indexOf(s[j]);
        
        if(isRepeat(s.slice(i,j))) {
            i += index + 1;
        }

        max = Math.max(max, j - i + 1)
    }

    return max;

    function isRepeat(s) {
        return s.length == Array.from(new Set(s.split(''))).length;
    }
};


动态规划,股票问题i



2021.05.20

No.122 买卖股票的最佳时机-ii

给定一个数组 prices ,其中 prices[i] 是一支给定股票第 i 天的价格。​ 设计一个算法来计算你所能获取的最大利润。你可以尽可能地完成更多的交易(多次买卖一支股票)。 ​

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

  ​

示例 1: ​

输入: prices = [7,1,5,3,6,4] 输出: 7 解释: 在第 2 天(股票价格 = 1)的时候买入,在第 3 天(股票价格 = 5)的时候卖出, 这笔交易所能获得利润 = 5-1 = 4 。   随后,在第 4 天(股票价格 = 3)的时候买入,在第 5 天(股票价格 = 6)的时候卖出, 这笔交易所能获得利润 = 6-3 = 3 。 示例 2: ​

输入: prices = [1,2,3,4,5] 输出: 4 解释: 在第 1 天(股票价格 = 1)的时候买入,在第 5 天 (股票价格 = 5)的时候卖出, 这笔交易所能获得利润 = 5-1 = 4 。   注意你不能在第 1 天和第 2 天接连购买股票,之后再将它们卖出。因为这样属于同时参与了多笔交易,你必须在再次购买前出售掉之前的股票。 示例 3: ​

输入: prices = [7,6,4,3,1] 输出: 0 解释: 在这种情况下, 没有交易完成, 所以最大利润为 0。   ​

提示: ​

1 <= prices.length <= 3 * 104 0 <= prices[i] <= 104 ​

来源:力扣(LeetCode) 链接:leetcode-cn.com/problems/be… 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

方案:

/*
 * @lc app=leetcode.cn id=122 lang=javascript
 *
 * [122] 买卖股票的最佳时机 II
 */

// @lc code=start
/**
 * @param {number[]} prices
 * @return {number}
 */
var maxProfit = function(prices) {
    let profit_in = -prices[0],
        profit_out = 0,
        n = prices.length;
    for(let i =0; i < n; i++) {
        profit_out = Math.max(profit_out, profit_in + prices[i]);
        profit_in = Math.max(profit_in,  profit_out - prices[i]);
    }
        
    return profit_out;  
};


动态规划,股票问题ii



2021.05.21

No.123 买卖股票的最佳时机-iii

给定一个数组,它的第 i 个元素是一支给定的股票在第 i 天的价格。​ 设计一个算法来计算你所能获取的最大利润。你最多可以完成 两笔 交易。 ​

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

  ​

示例 1: ​

输入:prices = [3,3,5,0,0,3,1,4] 输出:6 解释:在第 4 天(股票价格 = 0)的时候买入,在第 6 天(股票价格 = 3)的时候卖出,这笔交易所能获得利润 = 3-0 = 3 。   随后,在第 7 天(股票价格 = 1)的时候买入,在第 8 天 (股票价格 = 4)的时候卖出,这笔交易所能获得利润 = 4-1 = 3 。 示例 2: ​

输入:prices = [1,2,3,4,5] 输出:4 解释:在第 1 天(股票价格 = 1)的时候买入,在第 5 天 (股票价格 = 5)的时候卖出, 这笔交易所能获得利润 = 5-1 = 4 。     注意你不能在第 1 天和第 2 天接连购买股票,之后再将它们卖出。     因为这样属于同时参与了多笔交易,你必须在再次购买前出售掉之前的股票。 示例 3: ​

输入:prices = [7,6,4,3,1] 输出:0 解释:在这个情况下, 没有交易完成, 所以最大利润为 0。 示例 4: ​

输入:prices = [1] 输出:0   ​

提示: ​

1 <= prices.length <= 105 0 <= prices[i] <= 105 ​

来源:力扣(LeetCode) 链接:leetcode-cn.com/problems/be… 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

方案:

/*
 * @lc app=leetcode.cn id=123 lang=javascript
 *
 * [123] 买卖股票的最佳时机 III
 */

// @lc code=start
/**
 * @param {number[]} prices
 * @return {number}
 */
var maxProfit = function(prices) {
    //第一次 买入, 卖出的利润
    let profit_1_in = -prices[0], profit_1_out = 0;
    //继第一次之后,第二次买入卖出的利润
    let profit_2_in = -prices[0], profit_2_out = 0;
    let n = prices.length;
    for (let i = 1; i < n; i++){
        profit_2_out = Math.max(profit_2_out, profit_2_in + prices[i]);
        //第二次买入后的利润, 第一次卖出的利润 - prices[i]
        profit_2_in = Math.max(profit_2_in, profit_1_out - prices[i]);
        profit_1_out = Math.max(profit_1_out, profit_1_in + prices[i]);
        //第一次买入后,利润为 -prices[i]
        profit_1_in = Math.max(profit_1_in, -prices[i]);
    }
    return profit_2_out;
};


动态规划,股票问题iii



2021.05.24

No.188 买卖股票的最佳时机-iv

给定一个整数数组 prices ,它的第 i 个元素 prices[i] 是一支给定的股票在第 i 天的价格。​ 设计一个算法来计算你所能获取的最大利润。你最多可以完成 k 笔交易。 ​

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

  ​

示例 1: ​

输入:k = 2, prices = [2,4,1] 输出:2 解释:在第 1 天 (股票价格 = 2) 的时候买入,在第 2 天 (股票价格 = 4) 的时候卖出,这笔交易所能获得利润 = 4-2 = 2 。 示例 2: ​

输入:k = 2, prices = [3,2,6,5,0,3] 输出:7 解释:在第 2 天 (股票价格 = 2) 的时候买入,在第 3 天 (股票价格 = 6) 的时候卖出, 这笔交易所能获得利润 = 6-2 = 4 。 随后,在第 5 天 (股票价格 = 0) 的时候买入,在第 6 天 (股票价格 = 3) 的时候卖出, 这笔交易所能获得利润 = 3-0 = 3 。   ​

提示: ​

0 <= k <= 100 0 <= prices.length <= 1000 0 <= prices[i] <= 1000 ​

来源:力扣(LeetCode) 链接:leetcode-cn.com/problems/be… 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

方案:

/*
 * @lc app=leetcode.cn id=188 lang=javascript
 *
 * [188] 买卖股票的最佳时机 IV
 */

// @lc code=start
/**
 * @param {number} k
 * @param {number[]} prices
 * @return {number}
 */
var maxProfit = function(k, prices) {
    let n = prices.length;
    if (k > n / 2) {
        k = Math.floor(n/2);
    }
    let profits = [];
    for(let j=0; j <= k; j++) {
        profits[j] = {
            profits_out: 0,
            profits_in: -prices[0]
        }
    }
    
    for( let i = 0; i < n; i++ ) {
        for( let j=1; j <= k; j++ ) {
            profits[j] = {
                profits_out: Math.max(profits[j][`profits_out`], profits[j][`profits_in`] + prices[i]),
            profits_in: Math.max(profits[j][`profits_in`], profits[j-1][`profits_out`] - prices[i])
            }
        }
    }

    return profits[k][`profits_out`];
};

动态规划,股票问题iv



2021.05.26

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

给定一个整数数组,其中第 i 个元素代表了第 i 天的股票价格 。​ 设计一个算法计算出最大利润。在满足以下约束条件下,你可以尽可能地完成更多的交易(多次买卖一支股票): ​

你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)。 卖出股票后,你无法在第二天买入股票 (即冷冻期为 1 天)。 示例: ​

输入: [1,2,3,0,2] 输出: 3 解释: 对应的交易状态为: [买入, 卖出, 冷冻期, 买入, 卖出] ​

来源:力扣(LeetCode) 链接:leetcode-cn.com/problems/be… 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

方案:

/*
 * @lc app=leetcode.cn id=309 lang=javascript
 *
 * [309] 最佳买卖股票时机含冷冻期
 */

// @lc code=start
/**
 * @param {number[]} prices
 * @return {number}
 */
var maxProfit = function(prices) {
    let n = prices.length,
        profits_in = -prices[0],
        profits_out = 0,
        profits_freeze = 0;
    
    for( let i = 0; i < prices.length; i++ ) {
        let temp = profits_out;
        profits_out = Math.max(profits_out, prices[i] + profits_in);
        profits_in = Math.max(profits_in, profits_freeze - prices[i]);
        profits_freeze = temp;
    }

    return profits_out;
};

动态规划,股票问题v



2021.05.27

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

给定一个整数数组 prices,其中第 i 个元素代表了第 i 天的股票价格 ;整数 fee 代表了交易股票的手续费用。​ 你可以无限次地完成交易,但是你每笔交易都需要付手续费。如果你已经购买了一个股票,在卖出它之前你就不能再继续购买股票了。 ​

返回获得利润的最大值。 ​

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

  ​

示例 1: ​

输入:prices = [1, 3, 2, 8, 4, 9], fee = 2 输出:8 解释:能够达到的最大利润:
在此处买入 prices[0] = 1 在此处卖出 prices[3] = 8 在此处买入 prices[4] = 4 在此处卖出 prices[5] = 9 总利润: ((8 - 1) - 2) + ((9 - 4) - 2) = 8 示例 2: ​

输入:prices = [1,3,7,5,10,3], fee = 3 输出:6   ​

提示: ​

1 <= prices.length <= 5 * 104 1 <= prices[i] < 5 * 104 0 <= fee < 5 * 104 ​

来源:力扣(LeetCode) 链接:leetcode-cn.com/problems/be… 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

方案:

/*
 * @lc app=leetcode.cn id=714 lang=javascript
 *
 * [714] 买卖股票的最佳时机含手续费
 */

// @lc code=start
/**
 * @param {number[]} prices
 * @param {number} fee
 * @return {number}
 */
var maxProfit = function(prices, fee) {
    let n = prices.length,
        profits_in = 0 - prices[0],
        profits_out = 0;
    
    for( let i=0; i < prices.length; i++ ) {
        profits_out = Math.max(profits_out, prices[i] + profits_in - fee);
        profits_in = Math.max(profits_in, profits_out - prices[i]);
    }

    return profits_out < 0 ? 0 : profits_out;
};

动态规划,股票问题vi



总结:

  1. 股票问题关键在于对输入输出的状态进行判断转移,运用动态规划思想进行处理;
  2. 在动态规划实现中比较常见的是多维数组的逐步迭代,对于股票问题可以进行降维处理,优化效率​