其他分享
首页 > 其他分享> > 阿翰 剑指offer 之 Day 09 动态规划 2

阿翰 剑指offer 之 Day 09 动态规划 2

作者:互联网

目录

动态规划

1 连续子数组最大和

1. 暴力法

2. 分治法(官方题解--线段树)

3. 动态规划+临时变量

4. 动态规划+原地修改

2 礼物最大价值

1. 动态规划+二维数组

2. 多开一行一列0 优化代码


动态规划

1 连续子数组最大和

剑指 Offer 42. 连续子数组的最大和https://leetcode-cn.com/problems/lian-xu-zi-shu-zu-de-zui-da-he-lcof/

1. 暴力法

暴力法O(N2)O(1)

2. 分治法(官方题解--线段树)

分治法O(NlogN)O(logN)

这个分治方法类似于「线段树求解最长公共上升子序列问题」的 pushUp 操作。 也许读者还没有接触过线段树,没有关系,方法二的内容假设你没有任何线段树的基础。当然,如果读者有兴趣的话,推荐阅读线段树区间合并法解决多次询问的「区间最长连续上升序列问题」和「区间最大子段和问题」,还是非常有趣的。

首先定义一个操作 get(a, l, r) 表示查询 aa 序列 [l,r] 区间内的最大子段和,那么最终要求的答案就是 get(nums, 0, nums.size() - 1)。如何分治实现这个操作呢?

对于一个区间 [l,r],我们取 m=⌊(l+r)/2⌋,对区间 [l,m] 和 [m+1,r] 分治求解。当递归逐层深入直到区间长度缩小为 1 的时候,递归「开始回升」。这个时候考虑如何通过 [l,m] 区间的信息和 [m+1,r]区间的信息合并成区间 [l,r][l,r] 的信息。最关键的两个问题是:

对于一个区间 [l,r],我们可以维护四个量:

以下简称 [l,m] 为 [l,r] 的「左子区间」,[m+1,r] 为 [l,r] 的「右子区间」。考虑如何维护这些量呢?(如何通过左右子区间的信息合并得到 [l,r] 的信息)?

对于长度为 1 的区间 [i,i],四个量的值都和 nums[i] 相等。对于长度大于 1 的区间:

这样问题就得到了解决。

class Solution {
    public class Status {
        public int lSum, rSum, mSum, iSum;

        public Status(int lSum, int rSum, int mSum, int iSum) {
            this.lSum = lSum;
            this.rSum = rSum;
            this.mSum = mSum;
            this.iSum = iSum;
        }
    }

    public int maxSubArray(int[] nums) {
        return getInfo(nums, 0, nums.length - 1).mSum;
    }

    public Status getInfo(int[] a, int l, int r) {
        if (l == r) {
            return new Status(a[l], a[l], a[l], a[l]);
        }
        int m = (l + r) >> 1;
        Status lSub = getInfo(a, l, m);
        Status rSub = getInfo(a, m + 1, r);
        return pushUp(lSub, rSub);
    }

    public Status pushUp(Status l, Status r) {
        int iSum = l.iSum + r.iSum;
        int lSum = Math.max(l.lSum, l.iSum + r.lSum);
        int rSum = Math.max(r.rSum, r.iSum + l.rSum);
        int mSum = Math.max(Math.max(l.mSum, r.mSum), l.rSum + r.lSum);
        return new Status(lSum, rSum, mSum, iSum);
    }
}

3. 动态规划+临时变量

动态规划解析:

4. 动态规划+原地修改

空间复杂度降低:

复杂度分析:

2 礼物最大价值

剑指 Offer 47. 礼物的最大价值https://leetcode-cn.com/problems/li-wu-de-zui-da-jie-zhi-lcof/

1. 动态规划+二维数组

package jzof.Day09;

/**
 * @author ahan
 * @create_time 2021-11-11-11:45 上午
 * 在一个 m*n 的棋盘的每一格都放有一个礼物,每个礼物都有一定的价值(价值大于 0)。
 * 你可以从棋盘的左上角开始拿格子里的礼物,并每次向右或者向下移动一格、直到到达棋盘的右下角。
 * 给定一个棋盘及其上面的礼物的价值,请计算你最多能拿到多少价值的礼物?
 */
public class _47 {
    public static void main(String[] args) {
        int [][] nums = new int[][]{
                {1,3,1},
                {1,5,1},
                {4,2,1}
        };
        System.out.println(new _47().maxValue(nums));
    }
    public int maxValue(int[][] grid) {
        int m = grid.length;
        int n = grid[0].length;
        int[][] dp = new int[m][n];
        dp[0][0] = grid[0][0];
        for (int i = 1; i < m; i++) {
            dp[i][0] = dp[i-1][0] + grid[i][0];
        }
        for (int i = 1; i < n; i++) {
            dp[0][i] = dp[0][i-1] + grid[0][i];
        }
        for (int i = 1; i < m; i++) {
            for (int j = 1; j < n; j++) {
                dp[i][j] = Math.max(dp[i-1][j], dp[i][j-1])+grid[i][j];
            }
        }
        for (int i = 0; i < m; i++) {
            for (int j = 0; j < n; j++) {
                System.out.print(dp[i][j]+"\t");
            }
            System.out.println("  ");
        }
        return dp[m-1][n-1];
    }
}

居然和大佬实现的思路方法一毛一样 哈哈哈~ 不过大佬是原地修改。。。

 空间效率上也差的不多~

复杂度分析: 

2. 多开一行一列0 优化代码

public int maxValue(int[][] grid) {
        int row = grid.length;
        int column = grid[0].length;
        //dp[i][j]表示从grid[0][0]到grid[i - 1][j - 1]时的最大价值
        int[][] dp = new int[row + 1][column + 1];
        for (int i = 1; i <= row; i++) {
            for (int j = 1; j <= column; j++) {
                dp[i][j] = Math.max(dp[i - 1][j], dp[i][j - 1]) + grid[i - 1][j - 1];
            }
        }
        return dp[row][column];
    }

标签:nums,int,09,mSum,iSum,阿翰,offer,区间,dp
来源: https://blog.csdn.net/b_e_w/article/details/121260712