【Leecode笔记之Java】第十周(11.9-11.15)动态规划专题

动态规划往往用来优化递归问题。

什么问题可以用动态规划?
1.重叠子问题;(大问题的解依赖于小问题的解);
2.最优子结构(极值)。

主要分析三个东西:f(i),a(i)和f(i-1)之间的极值关系,也就是转移方程。

11.9

【第一题】最大子序和

在这里插入图片描述分析:在两个数相加的基础上,假如加上后一个数反而比之前两个数之和还小,则不加,整体窗口往后移动一格。
难点在于,如何将之前的最大子序列和记录下来,如何判断当前的子序列是最大子序列?

最大子序列的特点就是,首尾都为正数。直接根据规律找出最佳答案,而忽略对子序列的寻找比较。
当当前和位负数时,当前的子序列和不能对后面产生正向帮助,因此,必须重新确定序列首相。(无论之前子序列中包含的正数有多大,一旦子序列和加到负数,就要抛弃之前整个子序列,重新开始)

//执行用时:1 ms, 在所有 Java 提交中击败了95.33% 的用户
//内存消耗:38.4 MB, 在所有 Java 提交中击败了86.61% 的用户
class Solution{
	public int maxSubArray(int[] nums){
		int ans = nums[0];
		int sum = 0;
		for(int num:nums[]){
			if(sum > 0)
				sum += num;
			else
				sum = num;
			ans = Math.max(ans,sum);
		}
		return ans;
	}
}

【第二题】:按摩师

在这里插入图片描述

//执行用时:0 ms, 在所有 Java 提交中击败了100.00% 的用户
//内存消耗:35.8 MB, 在所有 Java 提交中击败了85.97% 的用户
class Solution{
	public int massage(int[] nums){
		//首先判断边界情况
		if(nums.length == 0) return 0;
		else if(nums.length == 1) return nums[0];
		else{
			//用一个新数组保存当前位置的最优解;
			int[] dp = new int[nums.length];
			dp[0] = nums[0];
			//当只有两个时,选时长大的那个;
			dp[1] = Math.max(nums[0],nums[1]);
			int max = dp[1];
			for(int i = 2;i < nums.length;i++){
				//比较dp[i-1]和dp[i-2]+nums[i];
				dp[i] = Math.max(dp[i-1],dp[i-2] + nums[i]);
				max = Math.max(max,dp[i]);
			}
		}
		return max;
	}
}

【第三题】除数博弈

在这里插入图片描述分析:偶数先手胜,奇数先手败。

class Solution{
	public boolean divisorGame(int N){
		return N % 2 ==0;
	}
}

【第四题】买卖股票的最佳时机

在这里插入图片描述
分析:前i天的最大收益 = max{前i-1天的收益,第i天的价格-前i天的最低价格};
步骤:1.记录 今天之间买入的最小值;
2.计算 今天之前最小值买入,今天卖出的获利(即今天的最大获利);
3.比较 每天的最大获利,取最大值即可。

//执行用时:2 ms, 在所有 Java 提交中击败了63.00% 的用户
//内存消耗:38.5 MB, 在所有 Java 提交中击败了78.29% 的用户
class Solution {
    public int maxProfit(int[] prices) {
        if(prices.length <=1){
            return 0;
        }
        //淦!一定要先判断边界才定义!!
        int max = 0,min = prices[0];
        for(int i = 1;i < prices.length;i++){
            max = Math.max(max,prices[i] - min);
            min = Math.min(min,prices[i]);
        }
        return max;
    }
}

【第五题】打家劫舍

在这里插入图片描述分析:今天之前窃取的最大金额=max{昨天之前窃取的最大金额,前天之前窃取的最大金额+今夜窃取的金额}。

【11.10】

第六题:使用最小花费爬楼梯

在这里插入图片描述分析:步长为1或2,选择花销最小的路来达到终点。
其实就是比较隔一个阶梯跨(步长为2)还是直接走(步长为1)花销小。
f(i) = min{f(i-1),f(i-2)+cost[i]};
难点在于1.第一步是走cost[0]还是cost[1]?
2.用min还是dp数组?他俩咋用的?
假如地面是第-1级台阶,台阶的数组由0开始计数,cost[-1] = 0(cost[n]表示踏上第n级台阶所要花费的经历)

在这里插入图片描述

状态转移方程:dp[i] = min(dp[i-1] + cost[i-1],dp[i-2] + cost[i-2])
初始条件:dp[0] = dp[1] = 0;

//执行用时:1 ms, 在所有 Java 提交中击败了100.00%的用户
//内存消耗:37.9 MB, 在所有 Java 提交中击败了94.94%的用户
class Solution{
	public int minCostClimbingStairs(int[] cost){
		int n = cost.length;
		int[] dp = new int [n+1];
		dp[0] = 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];
	}
}

第七题:区域和检索-数组不可变

分析:没有极值问题,感觉也可以不用动态规划,可以直接用递归。为了减少计算量,可以用一个数组存储每一步的值,例如sum[i]存储从0-i的所有元素和。

存储在哈希表中:

private Map<Pair<Integer,Integer>,Integer> map = new HashMap<>();

public NumArray(int[] nums){
	for(int i = 0;i < nums.length;i++){
		int sum = 0;
		for(int j = i;j < nums.length;j++){
			sum+=nums[i];
			map.put(Pair.create(i,j),sum);
		}
	}
}

public int sumRange(int i,int j){
	return map.get(Pair.create(i,j));
}
sums[i] += sums[i-1] + nums[i];
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值