动态规划力扣java

19 篇文章 0 订阅
  1. 区域和检索 - 数组不可变
    给定一个整数数组 nums,求出数组从索引 i 到 j(i ≤ j)范围内元素的总和,包含 i、j 两点。
    输入:
    [[[-2, 0, 3, -5, 2, -1]], [0, 2], [2, 5], [0, 5]]
    输出:
    [null, 1, -1, -3]
    要计算 sumRange(i,j),则需要计算数组 nums 在下标 jj 和下标i−1 的前缀和,然后计算两个前缀和的差。
    如果可以在初始化的时候计算出数组 nums 在每个下标处的前缀和,即可满足每次调用 sumRange 的时间复杂度都是 O(1)。
    具体实现方面,假设数组 nums 的长度为 n,创建长度为n+1 的前缀和数组 sums,对于 00≤i<n 都有sums[i+1]=sums[i]+nums[i],则当 0<i≤n 时,sums[i] 表示数组 nums 从下标 0 到下标i−1 的前缀和。
    将前缀和数组sums 的长度设为n+1 的目的是为了方便计算 sumRange(i,j),不需要对 i=0 的情况特殊处理。此时有:
    sumRange(i,j)=sums[j+1]−sums[i]
思路
 int[] nums = new int[10];
    public NumArray(int[] nums) {
        this.nums = nums;
    }
    
    public int sumRange(int i, int j) {
        int sum =0;
        for(int k =i;k <= j;k++){
            sum += nums[i];
        }
        return sum;
    }
    class NumArray {
    int[] sums;

    public NumArray(int[] nums) {
        int n = nums.length;
        sums = new int[n + 1];
        for (int i = 0; i < n; i++) {
            sums[i + 1] = sums[i] + nums[i];
        }
    }
    
    public int sumRange(int i, int j) {
        return sums[j + 1] - sums[i];
    }
}
  1. 使用最小花费爬楼梯
    数组的每个下标作为一个阶梯,第 i 个阶梯对应着一个非负数的体力花费值 cost[i](下标从 0 开始)。
    每当你爬上一个阶梯你都要花费对应的体力值,一旦支付了相应的体力值,你就可以选择向上爬一个阶梯或者爬两个阶梯。
    请你找出达到楼层顶部的最低花费。在开始时,你可以选择从下标为 0 或 1 的元素作为初始阶梯。
    示例 1:
    输入:cost = [10, 15, 20]
    输出:15
    解释:最低花费是从 cost[1] 开始,然后走两步即可到阶梯顶,一共花费 15 。
    示例 2:
    输入:cost = [1, 100, 1, 1, 1, 100, 1, 1, 100, 1]
    输出:6
    解释:最低花费方式是从 cost[0] 开始,逐个经过那些 1 ,跳过 cost[3] ,一共花费 6 。
    假设数组 cost 的长度为 n,则 n 个阶梯分别对应下标 0 到 n−1,楼层顶部对应下标 n,问题等价于计算达到下标 n的最小花费。可以通过动态规划求解。
    创建长度为 n+1 的数组dp,其中dp[i] 表示达到下标 i 的最小花费。
    由于可以选择下标 0 或 1 作为初始阶梯,因此有 dp[0]=dp[1]=0。
    当 2≤i≤n 时,可以从下标i−1 使用cost[i−1] 的花费达到下标 ii,或者从下标i−2 使用 cost[i−2] 的花费达到下标 i。为了使总花费最小,dp[i] 应取上述两项的最小值,因此状态转移方程如下:
    dp[i]=min(dp[i−1]+cost[i−1],dp[i−2]+cost[i−2])
    依次计算dp 中的每一项的值,最终得到的 dp[n] 即为达到楼层顶部的最小花费。
class Solution {
    public int minCostClimbingStairs(int[] cost) {
        int n = cost.length;
        int[] dp = new int[n + 1];
        dp[0] = dp[1] = 0;
        for (int i = 2; i <= n; i++) {
            dp[i] = Math.min(dp[i - 1] + cost[i - 1], dp[i - 2] + cost[i - 2]);
        }
        return dp[n];
    }
}
  1. 除数博弈
    爱丽丝和鲍勃一起玩游戏,他们轮流行动。爱丽丝先手开局。
    最初,黑板上有一个数字 N 。在每个玩家的回合,玩家需要执行以下操作:
    选出任一 x,满足 0 < x < N 且 N % x == 0 。
    用 N - x 替换黑板上的数字 N 。
    如果玩家无法执行这些操作,就会输掉游戏。
    只有在爱丽丝在游戏中取得胜利时才返回 True,否则返回 False。假设两个玩家都以最佳状态参与游戏。
    示例 1:
    输入:2
    输出:true
    示例 2:
    输入:3
    输出:false
思路
public boolean divisorGame(int N) {
        int count =1;
        for(int i=0;i< N;i++){
            if( N % i ==0){
                N -=i;
                count++;
            }
        }
        if(count %2 ==0) return false;
        return true;
    }
    class Solution {
    public boolean divisorGame(int N) {
        boolean[] f = new boolean[N + 5];

        f[1] = false;
        f[2] = true;
        for (int i = 3; i <= N; ++i) {
            for (int j = 1; j < i; ++j) {
                if ((i % j) == 0 && !f[i - j]) {
                    f[i] = true;
                    break;
                }
            }
        }

        return f[N];
    }
}

剑指 Offer 42. 连续子数组的最大和
输入一个整型数组,数组中的一个或连续多个整数组成一个子数组。求所有子数组的和的最大值。
要求时间复杂度为O(n)。
示例1:
输入: nums = [-2,1,-3,4,-1,2,1,-5,4]
输出: 6
解释: 连续子数组 [4,-1,2,1] 的和最大,为 6。
状态定义: 设动态规划列表 dpdp ,dp[i]dp[i] 代表以元素 nums[i]nums[i] 为结尾的连续子数组最大和。
为何定义最大和 dp[i]dp[i] 中必须包含元素 nums[i]nums[i] :保证 dp[i]dp[i] 递推到 dp[i+1]dp[i+1] 的正确性;如果不包含 nums[i]nums[i] ,递推时则不满足题目的 连续子数组 要求。
转移方程: 若dp[i−1]≤0 ,说明 dp[i - 1]dp[i−1] 对 dp[i] 产生负贡献,即 dp[i−1]+nums[i] 还不如 nums[i] 本身大。
当 dp[i−1]>0 时:执行 dp[i]=dp[i−1]+nums[i] ;
当 dp[i−1]≤0 时:执行 dp[i] = nums[i]dp[i]=nums[i] ;
初始状态:dp[0]=nums[0],即以 nums[0] 结尾的连续子数组最大和为 nums[0] 。
返回值: 返回 dpdp 列表中的最大值,代表全局最大值。
在这里插入图片描述

思路
public int maxSubArray(int[] nums) {
        int i=0,count =0;
        for(int j=1;j< nums.length;j++){
            int sum = nums[i] + nums[j];
            if(sum < count){
                i++;
                count = sum;
            }
        }
        return count; 
    }
    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;
    }
}
public int maxSubArray(int[] nums) {
        int[] dp = new int[nums.length];
        dp[0] = nums[0];
        int res =dp[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];
            }
            res = Math.max(res,dp[i]);
        }
        return res;
    }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值