zoukankan      html  css  js  c++  java
  • 剑指Offer

    剑指Offer中的动态规划除了一题hard(正则表达式匹配)都比较简单,要是面试只考到这种程度就好了...

    连续子数组的最大和

    这道题可以作为线性dp的模板。

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

    当以nums[i-1]为结尾的数组和(dp[i-1])大于0,对于以nums[i]为结尾的子数组(dp[i]),加上前一个数组和将得到更大的和;

    当以nums[i-1]为结尾的数组和(dp[i-1])小于0,对于以nums[i]为结尾的子数组(dp[i]),加上前一个数组和只会使结果比nums[i]自身小,所以不如舍弃前面的数。

    状态转移方程:

    dp[i-1] > 0,dp[i] = dp[i-1] + nums[i]

    dp[i-1] < 0,dp[i] = nums[i]

    dp[0] = nums[0]

    class Solution {
    public:
        int maxSubArray(vector<int>& nums) {
            int N = nums.size();
            vector<int> dp(N,0);
            dp[0] = nums[0];
            int res = dp[0];
            for(int i=1;i<N;++i) {
                if(dp[i-1] >= 0) {
                    dp[i] = dp[i-1] + nums[i];
                }
                else {
                    dp[i] = nums[i];
                }
                res = dp[i] > res ? dp[i] : res;
            }
            return res;
        }
    };

    空间复杂度优化

    上面的解法开辟了和nums等长的dp数组,而事实上dp数组是可以省略的。

    仔细看发现整个执行过程中,nums数组只被扫描了一次,且dp[i]的值仅和nums[i]有关,换言之,当程序开始计算dp[i]时,nums[i-1]以及之前的数据就变成无关紧要的了。所以,在计算出dp[i-1]时,用该结果覆盖nums[i-1]的值,当程序开始计算dp[i]时,直接取出nums[i-1]即可。

    将原数组作为dp数组,在线性dp中是常用的优化空间复杂度的方法。

    class Solution {
    public:
        int maxSubArray(vector<int>& nums) {
            int res = nums[0];
            for(int i = 1; i < nums.size(); ++i) {
                if(nums[i-1] >= 0) {
                    nums[i] = nums[i-1] + nums[i];
                }
                res = nums[i] > res ? nums[i] : res;
            }
            return res;
        }
    };

    股票的最大利润

    和最大子数组和的思路类似

    顺着一般的思路来模拟即可,甚至做完才发现是dp。

    状态转移方程:

    dp[imax(dp[i1]prices[i− min)

    class Solution {
    public:
        int maxProfit(vector<int>& prices) {
            if(prices.empty()) return 0;
            int min = prices[0];
            int res = 0;
            for(int i = 1; i < prices.size(); ++i) {
                if(prices[i] <= min) {
                    min = prices[i];
                } 
                else {
                    res = res > prices[i] - min ? res : prices[i] - min;
                }
            }
            return res;
        }
    };

    礼物的最大价值

    这种题目很容易当成贪心来做,但贪心并不能保证最优解,可以轻易举出反例。

    开辟二维dp数组,dp[i][j]表示从(0,0)出发选择礼物,到达(i,j)时累计的礼物最大价值。

    题目规定只有向右和向下两种方式,考虑四种情况:

    1.首行(i=0):此时上边没有礼物,不可能从上向下,所以无需考虑向下的情况。dp[0][j] = dp[0][j-1] + grid[0][j]

    2.首列(j=0):此时左边没有礼物,不可能从左向右,所以无需考虑向右的情况。dp[i][0] = dp[i-1][0] + grid[i][0]

    3.首元素(i=0,j=0):此时既不可能从上向下也不可能从左向右。dp[0][0] = grid[0][0]

    4.非首行且非首列(i != 0, j != 0):从上向下或从左向右得到。dp[i][j] = max(dp[i-1][j], dp[i][j-1]) + grid[i][j]

    分析的时候,刻意额外开辟了dp数组以和原数组区别开来。真正实现时,依然可以选择覆盖grid数组来优化空间复杂度。

    #define max(a, b) a > b ? a : b
    class Solution {
    public:
        int maxValue(vector<vector<int>>& grid) {
            int m = grid.size();
            int n = grid[0].size();
            for(int i = 0; i < m; i++) {
                for(int j = 0; j < n; j++) {
                    if(i == 0 && j == 0) continue;
                    if(i == 0) grid[i][j] += grid[i][j - 1] ;
                    else if(j == 0) grid[i][j] += grid[i - 1][j];
                    else grid[i][j] += max(grid[i][j - 1], grid[i - 1][j]);
                }
            }
            return grid[m - 1][n - 1];
        }
    };

    这道题也可以用搜索来解,代码略,下次整理记忆化搜索时附上。

  • 相关阅读:
    第10组 团队展示
    第一次结对编程作业
    13.Vue.js 组件
    12.Vue.js 表单
    11.Vue.js-事件处理器
    10.Vue.js 样式绑定
    9.Vue.js 监听属性
    8.Vue.js-计算属性
    7.循环语句
    6.Vue.js-条件与循环
  • 原文地址:https://www.cnblogs.com/CofJus/p/13411907.html
Copyright © 2011-2022 走看看