13.动态规划

一.连续子数组的最大和
1.题目描述:
输入一个整型数组,数组中的一个或连续多个整数组成一个子数组。求所有子数组的和的最大值。

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

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

3.解:
(1)我的答案:

class Solution {
    public int maxSubArray(int[] nums) {
        int max = nums[0];
        int[] dp = new int[nums.length];
        dp[0] = nums[0];
        for (int i = 1; i < nums.length; i++) {
            if (dp[i - 1] >= 0) dp[i] = dp[i - 1] + nums[i];
            else dp[i] = nums[i];
            max = Math.max(dp[i], max);
        }
        return max;
    }
}

(1)网友答案:

class Solution {
    public int maxSubArray(int[] nums) {
        int sum = 0, max = nums[0];
        for (int num : nums) {
            sum += num;
            max = Math.max(max, sum);
            if (sum < 0) sum = 0;
        }
        return max;
    }
}
class Solution {
    public int maxSubArray(int[] nums) {
        int res = nums[0];
        for(int i = 1; i < nums.length; i++) {
            nums[i] += Math.max(nums[i - 1], 0);
            res = Math.max(res, nums[i]);
        }
        return res;
    }
}

4.总结
网友答案的第一个解是最简单的解法,第二种是动态规划,它的动态规划空间复杂度比我的答案写的小,它是在原num数组的基础上更新子问题的解的。我的另外创建了一个缓存数组,存储每一个子问题的解。
动态规划:1.从问题中看出子问题解的定义是什么 2.建立缓存,记录每个子问题的解 3.回溯(从最小问题的解回溯,得到整个问题的解)

二.斐波那契数列
1.题目描述:
写一个函数,输入 n ,求斐波那契(Fibonacci)数列的第 n 项(即 F(N))。斐波那契数列的定义如下:
F(0) = 0, F(1) = 1
F(N) = F(N - 1) + F(N - 2), 其中 N > 1.
斐波那契数列由 0 和 1 开始,之后的斐波那契数就是由之前的两数相加而得出。
答案需要取模 1e9+7(1000000007),如计算初始结果为:1000000008,请返回 1。

2.示例:
输入:n = 2
输出:1

3.解:
(1)我的答案:

class Solution {
    public int fib(int n) {
        if(n<2) return n;
        else return (fib(n-1)+fib(n-2))%1000000007;
    }
}
class Solution {
    public int fib(int n) {
        if (n < 2) return n;
        int dp1 = 0, dp2 = 1;
        int temp = 0;
        for (int i = 1; i < n; i++) {
            temp = dp2;
            dp2 = (dp1 + dp2) % 1000000007;
            dp1 = temp;
        }
        return dp2;
    }
}

(2)网友答案:

class Solution {
    public int fib(int n) {
        if (n < 2) return n;
        int dp1 = 0, dp2 = 1;
        for (int i = 1; i < n; i++) {
            int q = dp2;
            dp2 = (dp2 + dp1) % (int)(1e9 + 7);
            dp1 = q;
        }
        return dp2;
    }
}

4.总结
本质上是考察递归,但是这道题目用递归,有的用例会超出时间限制。总之,知道如何递归就行了。如果不想使它超出时间限制,那么就使用我的第二个答案/网友的答案,即动态规划。

三.按摩师
1.题目描述:
一个有名的按摩师会收到源源不断的预约请求,每个预约都可以选择接或不接。在每次预约服务之间要有休息时间,因此她不能接受相邻的预约。给定一个预约请求序列,替按摩师找到最优的预约集合(总预约时间最长),返回总的分钟数。

2.示例:
输入: [1,2,3,1]
输出: 4
解释: 选择 1 号预约和 3 号预约,总时长 = 1 + 3 = 4。

输入: [2,1,4,5,3,1,1,3]
输出: 12
解释: 选择 1 号预约、 3 号预约、 5 号预约和 8 号预约,总时长 = 2 + 4 + 3 + 3 = 12。

3.解:
(1)我的答案:

class Solution {
    public int massage(int[] nums) {
        if (nums.length == 0) return 0;
        if (nums.length == 1) return nums[0];
        if (nums.length == 2) return Math.max(nums[0], nums[1]);
        int[] dp = new int[nums.length];
        dp[0] = nums[0];
        dp[1] = Math.max(nums[0], nums[1]);
        for (int i = 2; i < nums.length; i++) dp[i] = Math.max(dp[i - 1], dp[i - 2] + nums[i]);
        return dp[dp.length - 1];
    }
}

四.第 k 个数
1.题目描述:
有些数的素因子只有 3,5,7,请设计一个算法找出第 k 个数。注意,不是必须有这些素因子,而是必须不包含其他的素因子。例如,前几个数按顺序应该是 1,3,5,7,9,15,21。

2.示例:
输入: k = 5

输出: 9

3.解:
(1)我的答案:

class Solution {
    public int getKthMagicNumber(int k) {
        int[] uglyNums = new int[k]; // 丑数集合

        // 3 , 5 , 7 的指针
        int index3 = 0;
        int index5 = 0;
        int index7 = 0;

        uglyNums[0] = 1;

        for (int i = 1; i < k; i++) {
            uglyNums[i] = Math.min(uglyNums[index3] * 3, Math.min(uglyNums[index5] * 5, uglyNums[index7] * 7));
            if (uglyNums[i] == uglyNums[index3] * 3) index3++;
            if (uglyNums[i] == uglyNums[index5] * 5) index5++;
            if (uglyNums[i] == uglyNums[index7] * 7) index7++;
        }
        return uglyNums[k-1];
    }

}

4.总结
(1)动态规划的第一个问题,当前问题从哪里来。答案是从三个子问题来,即3,5,7的指针和所指向的元素值。
(2)动态规划的第二个问题,知道了从哪里来,我要做什么。答案是3,5,7指针指向的元素分别乘以3,5,7,选取最小值作为当前元素,并改变对应指针的值指向当前元素。

五.最大黑方阵
1.题目描述:
给定一个方阵,其中每个单元(像素)非黑即白。设计一个算法,找出 4 条边皆为黑色像素的最大子方阵。

返回一个数组 [r, c, size] ,其中 r, c 分别代表子方阵左上角的行号和列号,size 是子方阵的边长。若有多个满足条件的子方阵,返回 r 最小的,若 r 相同,返回 c 最小的子方阵。若无满足条件的子方阵,返回空数组。

2.示例:
输入:
[
[1,0,1],
[0,0,1],
[0,0,1]
]
输出: [1,0,2]
解释: 输入中 0 代表黑色,1 代表白色,标粗的元素即为满足条件的最大子方阵

输入:
[
[0,1,1],
[1,0,1],
[1,1,0]
]
输出: [0,0,1]

3.解:

class Solution {
    private int[] res;

    public int[] findSquare(int[][] matrix) {
        int rowLen = matrix.length;
        int colLen = matrix[0].length;
        int[][] downMax = new int[rowLen][colLen];//记录矩阵中每个节点向下连续为0的长度
        int[][] rightMax = new int[rowLen][colLen];//记录矩阵中每个节点向右连续为0的长度
        int[] res = new int[3];//结果集

        //遍历,填充downMax和rightMax
        for (int i = rowLen - 1; i >= 0; i--) {
            for (int j = colLen - 1; j >= 0; j--) {
                if (matrix[i][j] == 0) {
                    downMax[i][j] = 1;
                    if (i < rowLen - 1) downMax[i][j] += downMax[i + 1][j];
                    rightMax[i][j] = 1;
                    if (j < colLen - 1) rightMax[i][j] += rightMax[i][j + 1];
                }
            }
        }

        //遍历matrix中的每个元素,判断以当前遍历元素为左上角的每个正方形矩阵是否四条边全为0,且尺寸大于res当前所记录的,就更新结果集
        for (int i = 0; i < rowLen; i++) {
            for (int j = 0; j < colLen; j++) {
                if (matrix[i][j] == 0) {
                    int curSize = res[2];//当前结果集中符合条件的正方形矩阵的边长
                    int size = Math.min(downMax[i][j], rightMax[i][j]);//取当前元素向下和向右连续为0的长度,取两个值的最小值作为候选的正方形矩阵的边长
                    // 取最小值而不是最大值的原因是,取最小值才能保证每个边上的元素都为0,而且取了最小值也只能保证两条边符合条件,接下来的操作就是判断另外两条边也符合条件
                    for (int tempSize = size; tempSize > curSize; tempSize--) {
                        //如果另外两条边的连续为0的长度大于等于tempSize,则以temp为边长的正方形矩阵四条边都满足条件,则更新结果集
                        if (downMax[i][j + tempSize - 1] >= tempSize && rightMax[i + tempSize - 1][j] >= tempSize) {
                            res[0] = i;
                            res[1] = j;
                            res[2] = tempSize;
                            break;
                        }
                    }
                }

            }
        }

        if (res[2] == 0) return new int[]{};

        return res;
    }
}

4.总结
这题的题解我觉得不是动态规划,硬要说是动态规划,可能只有对矩阵中每个元素,判断以它为左上角的每个矩阵是否符合条件时,才勉强算是动态规划。

全部评论

相关推荐

投递小红书等公司10个岗位
点赞 评论 收藏
分享
点赞 收藏 评论
分享
牛客网
牛客企业服务