【剑指Offer】动态规划(1)

剑指 Offer 42. 连续子数组的最大和

输入一个整型数组,数组中的一个或连续多个整数组成一个子数组。求所有子数组的和的最大值。

要求时间复杂度为O(n)。

示例1:

输入: nums = [-2,1,-3,4,-1,2,1,-5,4]
输出: 6
解释: 连续子数组 [4,-1,2,1] 的和最大,为 6。

提示:

  • 1 <= arr.length <= 10^5
  • -100 <= arr[i] <= 100
class Solution {
public:
    int maxSubArray(vector<int>& nums) {
        int ans = nums[0], cur = nums[0], n = nums.size();
        for(int i = 1; i < n; ++ i){
            if(cur < 0)cur = 0;
            cur += nums[i];
            ans = max(ans, cur);
        }
        return ans;
    }
};

算法思路:

假设和最大的子串abc组成,b是前面部分,c是后面部分,则b子串的和必须大于0,否则c串的和会比a串和大,与结果矛盾,因此我们可以从第一个元素开始往后计算和,当和小于0时说明该子串不能成为其它串的前缀,和重置为0,继续往后遍历。定一个长度为n的数组dpdp[i]表示以nums[i]结尾的子串的最大值,最后取dp的最大值就是答案。又因为dp[i]的值相互独立,我们可以在遍历时就开始比较,定义ans表最大值,减少空间开销。

题目一结果

剑指 Offer 47. 礼物的最大价值

在一个 m*n 的棋盘的每一格都放有一个礼物,每个礼物都有一定的价值(价值大于0)。你可以从棋盘的左上角开始拿格子里的礼物,并每次向右或者向下移动一格、直到到达棋盘的右下角。给定一个棋盘及其上面的礼物的价值,请计算你最多能拿到多少价值的礼物?

示例 1:

输入: 
[
  [1,3,1],
  [1,5,1],
  [4,2,1]
]
输出: 12
解释: 路径 1→3→5→2→1 可以拿到最多价值的礼物

提示:

  • 0 < grid.length <= 200
  • 0 < grid[0].length <= 200
解法一:
class Solution {
public:
    int maxValue(vector<vector<int>>& grid) {
        queue<pair<int, int>> q;
        q.push(pair<int, int>(0, 0));
        int ans = -INT_MAX, n = grid.size(), m = grid[0].size();
        int dirs[2][2] = {{0, 1}, {1, 0}}, pred[2][2] = {{-1, 0}, {0, -1}};
        vector<vector<bool>> vis(n, vector<bool>(m));
        vis[0][0] = true;
        while(q.empty() == false){
            auto p = q.front();
            q.pop();
            int x = p.first, y = p.second, best = grid[x][y];
            ans = max(ans, best);
            for(int i = 0; i < 2; ++ i){
                int ox = x + pred[i][0], oy = y + pred[i][1];
                int nx = x + dirs[i][0], ny = y + dirs[i][1];
                if(ox >= 0 && ox < n && oy >= 0 && oy < m){
                    best = max(best, grid[x][y] + grid[ox][oy]);
                }
                if(nx >= 0 && nx < n && ny >= 0 && ny < m){
                    if(vis[nx][ny] == false){
                        q.push(pair<int, int>(nx, ny));
                        vis[nx][ny] = true;
                    }
                }
            }
            grid[x][y] = best;
        }
        return grid[n - 1][m - 1];
    }
};

算法思路:

题目表示每次只能向右和向下走,可以把整个棋盘看成一张图,就如例子那样

	[1  3  1]						[     1     ]
  	[1  5  1]			=>			[   1   3   ]
  	[4  2  1]						[ 4   5   1 ]
  									[   2   1   ]
                                    [     1     ]

从菱形的最上层开始遍历,一直往下走,每个结点的值就是上面一或两个结点的最大值加上本身,最底层结点的值就是最大路径,相当于暴力广度优先搜索,期间还需要去重。

解法一结果

解法二:
class Solution {
public:
    int maxValue(vector<vector<int>>& grid) {
        int n = grid.size(), m = grid[0].size();
        for(int i = 0; i < n; ++ i){
            for(int j = 0; j < m; ++ j){
                if(i != 0 || j != 0){
                    if(i == 0){
                        grid[i][j] = grid[i][j] + grid[i][j - 1];
                    }else if(j == 0){
                        grid[i][j] = grid[i][j] + grid[i - 1][j];
                    }else grid[i][j] = max(grid[i][j - 1], grid[i - 1][j]) + grid[i][j];
                }
            }
        }
        return grid[n - 1][m - 1];
    }
};

算法思路:

由思路一可看到,这道题最重要的就是一个结点的值是依靠其上面两个结点的值,这两个结点说白了就是上边和左边两个结点,因此我们不需进行广度优先搜索,只需要从最左上角的结点开始,从左到右从上往下遍历,这样就使遍历到的结点的上边和左边结点先遍历到,可以直接得到结果,矩阵的最右下角的值就是答案。

解法二结果

【剑指Offer】系列:
【剑指Offer】栈
【剑指Offer】链表
【剑指Offer】字符串
【剑指Offer】查找算法
【剑指Offer】查找算法(1)
【剑指Offer】搜索与回溯算法
【剑指Offer】查找算法(1)
【剑指Offer】搜索与回溯算法
【剑指Offer】搜索与回溯算法(1)
【剑指Offer】动态规划

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值