题目来自《代码随想录》
文章目录
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/
- 求组合数
- 初始化要初始化最大!因为要求min!
- 如果dp[j-coins[i]]就是max,那没有计算的必要
- 最后输出不是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]);
}