代码随想录——动态规划(二):打家劫舍系列,买卖股票系列

题目来自《代码随想录》

377. 组合总和 Ⅳ

完全背包问题看要求求的是组合数还是排列数

	//如果求组合数就是外层for循环遍历物品,内层for遍历背包。
	//如果求排列数就是外层for遍历背包,内层for循环遍历物品。
	public int combinationSum4(int[] nums, int target) {
		int[] dp = new int[target+1];
		dp[0] = 1;
		
		for(int j=0; j<target+1; j++){
			for(int i=0; i<nums.length; i++){
				if(j>=nums[i]) dp[j] += dp[j-nums[i]];
			}
		}
		return dp[target];
	}

70. 爬楼梯

	public int climbStairs(int n) {
        int[] stairs = new int[]{1,2};//每次可爬楼梯数
		int[] dp = new int[n+1];
		dp[0] = 1;
		for(int j=0; j<n+1; j++){
			for(int i=0; i<stairs.length; i++){
				if(j>=stairs[i]) dp[j] += dp[j-stairs[i]];
			}
		}
		return dp[n];
    }

322. 零钱兑换

https://leetcode-cn.com/problems/coin-change/

  1. 求组合数
  2. 初始化要初始化最大!因为要求min!
  3. 如果dp[j-coins[i]]就是max,那没有计算的必要
  4. 最后输出不是max才输出,否则输出-1
	//dp[i]:凑成金额所需的 最少的硬币个数
	//递推公式:dp[j] = min(dp[j - coins[i]] + 1, dp[j]);
	//dp[j]必须初始化为一个最大的数,否则就会在min(dp[j - coins[i]] + 1, dp[j])比较的过程中被初始值覆盖。!!!
	public int coinChange(int[] coins, int amount) {
		int[] dp = new int[amount+1]; 
        //初始化dp数组为最大值
        for (int j = 0; j < dp.length; j++) {
            dp[j] = Integer.MAX_VALUE;
        }
        //当金额为0时需要的硬币数目为0
        dp[0] = 0;
		for(int i=0; i<coins.length; i++){
			//System.out.println("当前i=" + i);
			for(int j=coins[i]; j<amount+1; j++){
				//只有dp[j-coins[i]]不是初始最大值时,该位才有选择的必要
				if (dp[j - coins[i]] != Integer.MAX_VALUE) dp[j] = Math.min(dp[j], dp[j-coins[i]]+1);
			}
		}
		return dp[amount] == Integer.MAX_VALUE ? -1 : dp[amount];
	}

279. 完全平方数

	//放入包中的物品是i*i
	public int numSquares(int n) {
		int[] dp = new int[n+1];
		int max = Integer.MAX_VALUE;
		for(int i=0; i<n+1; i++) dp[i]=max;
		dp[0] = 0;
		
		for(int i=1; i*i<=n; i++){
			for(int j=i*i; j<n+1; j++){
				if(dp[j-i*i] != max) dp[j] = Math.min(dp[j], dp[j-i*i]+1);
			}
		}		
		return dp[n]==max?-1:dp[n];
	}

139. 单词拆分

https://leetcode-cn.com/problems/word-break/

	/*
	 * 每个单词是物品,s是背包,物品能否把背包装满
	 * 例:s = "leetcode", wordDict = ["leet", "code"],返回true
	 * 例:s = "applepenapple", wordDict = ["apple", "pen"],返回true
	 * 
	 * dp[i]的含义:字符串长度为i的话,dp[i]为true,表示可以拆分为一个或多个在字典中出现的单词。
	 * 执行用时:11 ms, 在所有 Java 提交中击败了14.20%的用户
	 * 内存消耗:41.5 MB, 在所有 Java 提交中击败了43.14%的用户
	 */
	public boolean wordBreak(String s, List<String> wordDict) {
		boolean[] dp = new boolean[s.length()+1];
		dp[0] = true;
		for (int i = 1; i <= s.length(); i++) {//分割的最后一个字符的位置
            for (int j = 0; j < i; j++) {//当前单词开始 的位置
				/*
				 * 成为true的条件
				 * 1. 包含这个单词:wordDict.contains(s.substring(j,i))
				 * 2. 除了这个单词的前一部分也是true:valid[j]
				 */
            	if(wordDict.contains(s.substring(j,i)) && dp[j]) dp[i] = true;           	
			}
		}
		return dp[s.length()];
	}

198. 打家劫舍

	/*
	 * dp[i]:考虑下标i(包括i)以内的房屋,最多可以偷窃的金额为dp[i]。
	 */
	public int rob(int[] nums) {
		int[] dp = new int[nums.length+1];
		dp[1] = nums[0];//第一家肯定偷了收益最高
		//show(nums);
		for(int i=2; i<nums.length+1; i++){
			//System.out.println(i);
			/*
			 * 1. 偷:dp[i-2]+nums[i]
			 * 2. 不偷:dp[i-1]
			 * 上面两个求max
			 */
			dp[i] = Math.max(dp[i-2]+nums[i-1], dp[i-1]);
		}
		return dp[nums.length];
	}

213. 打家劫舍 II

和上面一样,成环,两次考虑求max

	public int rob(int[] nums) {
        if (nums == null || nums.length == 0)
            return 0;
        int len = nums.length;
        if (len == 1)
            return nums[0];
        return Math.max(robAction(nums, 0, len - 1), robAction(nums, 1, len));
    }

    int robAction(int[] nums, int start, int end) {
        int x = 0, y = 0, z = 0;
        for (int i = start; i < end; i++) {
            y = z;
            z = Math.max(y, x + nums[i]);
            x = y;
        }
        return z;
    }

337. 打家劫舍 III

	/*
	 * 树形dp
	 * dp[2]当中存两个值,这个结点偷了的最多的钱,以及不透这个结点最多多少钱
	 */	
	//int[] dp = new int[2];//dp[0]偷的最多钱,dp[1]不偷的最多钱
	public int rob(TreeNode root) {
		int[] res = robaction(root);
        return Math.max(res[0], res[1]);
	}
	/*
	// 不偷:Max(左孩子不偷,左孩子偷) + Max(又孩子不偷,右孩子偷)
    // root[0] = Math.max(rob(root.left)[0], rob(root.left)[1]) +
    // Math.max(rob(root.right)[0], rob(root.right)[1])
    // 偷:左孩子不偷+ 右孩子不偷 + 当前节点偷
	 */
	public int[] robaction(TreeNode root) {
		int res[] = new int[2];
		if(root == null) return res;

		//后序遍历
		int[] l = robaction(root.left);
		int[] r = robaction(root.right);
		
		//如果当前偷,左不偷+右不偷+自己
		res[0] = l[1] + r[1] + root.val;
		//如果当前不偷,左右偷的max
		res[1] = Math.max(l[0], l[1]) + Math.max(r[0], r[1]);
		
		System.out.println("当前偷的结点为:" + root.name + ",val = " + root.val);
		System.out.println("偷:" + res[0] + ",不偷:" + res[1]);	
		
		return res;
	}

121. 买卖股票的最佳时机

https://leetcode-cn.com/problems/best-time-to-buy-and-sell-stock/

	/*
	 * 1. 暴力超时,202 / 211 个通过测试用例
	 */
	public int maxProfit(int[] prices) {
		int ans = Integer.MIN_VALUE;		
		for(int i=0; i<prices.length; i++){//买入天
			for(int j=i; j<prices.length; j++){//卖出天
				if((prices[j] - prices[i]) > ans) ans = prices[j] - prices[i];
			}
		}
		return ans;
	}
	
	@Test
	public void test(){
		int[] nums1 = new int[]{7,1,5,3,6,4};
		System.out.println(maxProfit2(nums1));
	}
	
	/*
	 * 2. 动态规划:差的累积什么时候变成负数了,什么时候重新买入
	 * 执行用时:3 ms, 在所有 Java 提交中击败了32.63%的用户
	 * 内存消耗:57.5 MB, 在所有 Java 提交中击败了45.50%的用户
	 */
	public int maxProfit2(int[] prices) {
		int difsum = 0;
		int res = 0;
		for(int i=1; i<prices.length; i++){
			difsum += (prices[i] - prices[i-1]);
			if(difsum < 0) difsum = 0;
			res = Math.max(difsum, res);
			System.out.println("res:" + res + ", difsum:" + difsum);
		}
		return res;
	}

122. 买卖股票的最佳时机 II

https://leetcode-cn.com/problems/best-time-to-buy-and-sell-stock-ii/

	public int maxProfit(int[] prices) {
		int res = 0;
		for(int i=1; i<prices.length; i++) {
			int cur = prices[i]-prices[i-1];
			if(cur > 0) res+=cur;
		}
		return res;
    }

123. 买卖股票的最佳时机III

https://leetcode-cn.com/problems/best-time-to-buy-and-sell-stock-iii/

	/*
	 * 一天一共就有五个状态,
	 * 1. 没有操作
	 * 2. 第一次买入
	 * 3. 第一次卖出
	 * 4. 第二次买入
	 * 5. 第二次卖出
	 * dp[i][j]中 i表示第i天,j为 [0 - 4] 五个状态,dp[i][j]表示第i天状态j所剩最大现金。
	 */
	public int maxProfit(int[] prices) {
		int[][] dp = new int[prices.length][5];
		
		dp[0][0] = 0; 			//第一天无操作
		dp[0][1] = -prices[0];  //第一天买入
		dp[0][2] = 0; 			//第一天卖出
		dp[0][3] = -prices[0];  //第二天买入
		dp[0][4] = 0; 			//第二天卖出
		
		for(int i=1; i<prices.length; i++){
			//第i天持有一次买入的情况:1.前一天买了 2. 今天买入,两个求max
			dp[i][1] = Math.max(dp[i-1][1], dp[i-1][0]-prices[i]);
			//第i天持有一次卖出的情况:1.前一天卖了 2. 今天卖,两个求max
			dp[i][2] = Math.max(dp[i-1][2], dp[i-1][1]+prices[i]);
			//第i天持有二次买入的情况:1.前一天买了 2. 今天买入,两个求max
			dp[i][3] = Math.max(dp[i-1][3], dp[i-1][2]-prices[i]);
			//第i天持有二次卖出的情况:1.前一天卖了 2. 今天卖,两个求max
			dp[i][4] = Math.max(dp[i-1][4], dp[i-1][3]+prices[i]);
		}
		//show(dp);
		return dp[prices.length-1][4];
	}

188. 买卖股票的最佳时机 IV

https://leetcode-cn.com/problems/best-time-to-buy-and-sell-stock-iv/

	/*
	 * 一天一共就有五个状态,
	 * 1. 没有操作
	 * 2. 第一次买入
	 * 3. 第一次卖出
	 * 4. 第二次买入
	 * 5. 第二次卖出
	 * dp[i][j]中 i表示第i天,j为 [0 - 4] 五个状态,dp[i][j]表示第i天状态j所剩最大现金。
	 */
	public int maxProfit(int k, int[] prices) {
		if( k==0 || prices.length==0) return 0;
		int len = prices.length;
		int[][] dp = new int[len][2*k+1];
		
		for(int i=0; i<2*k+1; i++){ //第一天初始化
			if(i%2 != 0) dp[0][i] = -prices[0];
		}
		
		for(int i=1; i<len; i++){
			for(int j=1; j<5; j++){
				if(j%2 == 1){//买入的情况
					dp[i][j] = Math.max(dp[i-1][j], dp[i-1][j-1]-prices[i]);
				}else{//卖出的情况
					dp[i][j] = Math.max(dp[i-1][j], dp[i-1][j-1]+prices[i]);
				}
			}
		}				
		return dp[len-1][2*k];
	}

309. 最佳买卖股票时机含冷冻期

https://leetcode-cn.com/problems/best-time-to-buy-and-sell-stock-with-cooldown/

	/*
	 * 四个状态
	 * 1.买入股票状态
	 * 2.两天前就卖出了股票,度过了冷冻期,一直没操作,今天保持卖出股票状态
	 * 3.今天卖出了股票
	 * 4.冷冻期状态
	 */
	public int maxProfit(int[] prices) {
		int len = prices.length;
		int[][] dp = new int[len][4];
		
		dp[0][0] = -prices[0]; //第一天买入股票,其他三天都是0
		
		for(int i=1; i<len; i++){
			for(int j=0; j<4; j++){
				/*
				 * 一、买入股票状态
				 * 	1.今天买入的股票:-prices[i]
				 * 		前一天是冷冻期:dp[i - 1][3] - prices[i]
				 * 		前一天是状态二:dp[i - 1][1] - prices[i]
				 * 	2.保持了买入状态:dp[i-1][0]
				 */
				dp[i][0] = Math.max(dp[i-1][0], Math.max(dp[i-1][3], dp[i-1][1])-prices[i]);
				
				/*
				 * 二、度过冷冻期的卖出状态
				 * 	1.前一天是冷冻期:dp[i-1][1]
				 * 	2.前一天是状态二:dp[i-1][3]
				 */
				dp[i][1] = Math.max(dp[i-1][1], dp[i-1][3]);
				
				/*
				 * 三、今天卖出,只有一个状态
				 */
				dp[i][2] = dp[i-1][0] + prices[i];
				
				/*
				 * 四、冷冻期,只有一个状态,前一天卖出了股票
				 */
				dp[i][3] = dp[i-1][2];
			}
		}
				
		return Math.max(dp[len-1][1], Math.max(dp[len-1][2], dp[len-1][3]));
	}

714. 买卖股票的最佳时机含手续费

https://leetcode-cn.com/problems/best-time-to-buy-and-sell-stock-with-transaction-fee/

	/*
	 * 第i-1天就持有股票,那么就保持现状,所得现金就是昨天持有股票的所得现金 
	 * 即:dp[i - 1][0]
	 * 第i天买入股票,所得现金就是昨天不持有股票的所得现金减去 今天的股票价格 
	 * 即:dp[i - 1][1] - prices[i]
	 * 
	 * 
	 * 第i-1天就不持有股票,那么就保持现状,所得现金就是昨天不持有股票的所得现金
	 *  即:dp[i - 1][1]
	 *  第i天卖出股票,所得现金就是按照今天股票价格卖出后所得现金,注意这里需要有手续费了
	 *  即:dp[i - 1][0] + prices[i] - fee
	 */
	public int maxProfit1(int[] prices, int fee) {
	    int len = prices.length;
	    // 0 : 持股(买入)
	    // 1 : 不持股(售出)
	    // dp 定义第i天持股/不持股 所得最多现金
	    int[][] dp = new int[len][2];
	    dp[0][0] = -prices[0];
	    for (int i = 1; i < len; i++) {
	        dp[i][0] = Math.max(dp[i - 1][0], dp[i - 1][1] - prices[i]);
	        dp[i][1] = Math.max(dp[i - 1][0] + prices[i] - fee, dp[i - 1][1]);
	    }
	    return Math.max(dp[len - 1][0], dp[len - 1][1]);
	}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

平什么阿

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值