1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| class Solution { public int findLength(int[] nums1, int[] nums2) {
int [][]dp = new int[nums1.length+1][nums2.length+1]; int res=0; for(int i=0;i<nums1.length;i++){ for(int j=0;j<nums2.length;j++){ if(nums1[i]==nums2[j]){ dp[i+1][j+1]=dp[i][j]+1; res=Math.max(res,dp[i+1][j+1]); } } } return res; } }
|
题目:
给你一个整数数组 prices ,其中 prices[i] 表示某支股票第 i 天的价格。
在每一天,你可以决定是否购买和/或出售股票。你在任何时候 最多 只能持有 一股 股票。你也可以先购买,然后在 同一天 出售。
返回 你能获得的 最大 利润
思路:
- 贪心:低价买入,如果第二天高价就卖出,局部最优解
1 2 3 4 5 6 7 8 9 10 11 12 13
| class Solution { public int maxProfit(int[] prices) { int res =0; for(int i=1;i<prices.length;i++){ if(prices[i]-prices[i-1]>0){ res+=prices[i]-prices[i-1]; } } return res; } }
|
- 动态规划:状态:有股票or没股票,dp数组含义:第i天持有or没持有股票的最大利润,状态推导
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| class Solution { public int maxProfit(int[] prices) { int [][] dp = new int[prices.length][2]; dp[0][0] = -prices[0]; dp[0][1] = 0; for(int i=1;i<prices.length;i++){ dp[i][0]=Math.max(dp[i-1][0],dp[i-1][1]-prices[i]); dp[i][1]=Math.max(dp[i-1][1],dp[i-1][0]+prices[i]); } return dp[prices.length-1][1]; } }
|

思路:DP,四部曲 ,数组含义,初始化,状态,推导
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| class Solution { public int uniquePaths(int m, int n) { int [][] dp = new int[m][n]; dp[0][0]=1; for(int i=1;i<m;i++){ dp[i][0]=1; } for(int j=1;j<n;j++){ dp[0][j]=1; } for(int i=1;i<m;i++){ for(int j=1;j<n;j++){ dp[i][j]=dp[i-1][j]+dp[i][j-1]; } } return dp[m-1][n-1]; } }
|
思路:
- 背诵 哈哈哈
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| class Solution { public int minDistance(String word1, String word2) { int [][]dp=new int[word1.length()+1][word2.length()+1];
for(int i=0;i<=word1.length();i++){ dp[i][0]=i; } for(int j=0;j<=word2.length();j++){ dp[0][j]=j; } for(int i=1;i<=word1.length();i++){ for(int j=1;j<=word2.length();j++){ if(word1.charAt(i-1)==word2.charAt(j-1)){ dp[i][j]=dp[i-1][j-1]; } else dp[i][j]=Math.min(dp[i-1][j-1],Math.min(dp[i-1][j],dp[i][j-1]))+1; } } return dp[word1.length()][word2.length()]; } }
|
思路:
- 动态规划,思考dp数组含义,当前状态是怎么由前一个状态推导过来的
- //两种情况:比如我在三阶梯,我可以从一阶梯一下子两步上来,以及从二阶梯一步上来
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| class Solution { public int climbStairs(int n) { if(n < 2) return n; int []dp = new int[n+1]; dp[0] = 1; dp[1] = 1; dp[2] = 2; for(int i = 3;i <= n;i++){ dp[i] = dp[i-1] + dp[i-2]; } return dp[n]; } }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| class Solution { public int coinChange(int[] coins, int amount) { if(amount <= 0){ return 0; } int []dp = new int[amount+1]; Arrays.fill(dp,Integer.MAX_VALUE); dp[0] = 0; for(int i = 0;i < dp.length;i++){ for(int j = 0;j<coins.length;j++){ if( i - coins[j] >= 0 && dp[i-coins[j]] != Integer.MAX_VALUE){ dp[i] = Math.min(dp[i-coins[j]] + 1, dp[i]); } } } System.out.println(Arrays.toString(dp)); return dp[amount] == Integer.MAX_VALUE? -1: dp[amount]; } }
|
思路:动态规划
就嗯记,dp思路 相邻三个矩形边长的最小值+1
dp数组含义即 以matrix[i][j] 为右下角为右下角的最大正方形边长
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| class Solution { public int maximalSquare(char[][] matrix) { int n = matrix.length; int m = matrix[0].length; int[][] dp = new int[n][m]; int res = 0; for (int i = 0; i < n; i++) { for (int j = 0; j < m; j++) { if(matrix[i][j] == '1') { if(i == 0|| j == 0){ dp[i][j] = 1; } else dp[i][j] = Math.min(dp[i - 1][j - 1], Math.min(dp[i][j - 1], dp[i - 1][j])) + 1; res = Math.max(res, dp[i][j]); } } } return res * res; } }
|
思路:
- 动规,详情见代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| class Solution { public int rob(int[] nums) { if(nums.length == 1) return nums[0]; int []dp = new int[nums.length]; dp[0] = nums[0]; dp[1] = Math.max(nums[0], nums[1]); int res = dp[1]; for(int i = 2;i < nums.length; i++){ dp[i] = Math.max(dp[i - 2] + nums[i], dp[i - 1]); res = Math.max(res, dp[i]); } return res; } }
|
输出路径
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| class Solution { public int rob(int[] nums) { if(nums.length == 1) return nums[0]; int []dp = new int[nums.length]; dp[0] = nums[0]; dp[1] = Math.max(nums[0], nums[1]); int res = dp[1]; List<Integer> resList = new ArrayList<>(); for(int i = 2;i < nums.length; i++){ if(dp[i - 2] + nums[i] > dp[i - 1]){ if(i == 2) resList.add(dp[0]); resList.add(nums[i]); } dp[i] = Math.max(dp[i-2] + nums[i], dp[i - 1]); res = Math.max(res, dp[i]); } System.out.println(resList); return res; } }
|
思路:
- DP做法:看成背包问题,即s是否可以被物品wordDict装满
- 那么这时候dp数组的含义:dp[i] 表示0….i 范围的s是否可以被worddict里的单词分隔
- 这种做法时间复杂度 O()?不太行哦
- 时间复杂度:O(n³)
- n = 字符串长度
- 因为三层操作:外层 i、内层 j、substring 复制
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| class Solution { public boolean wordBreak(String s, List<String> wordDict) { HashSet<String> set = new HashSet(wordDict); boolean[] dp = new boolean[s.length()]; for (int i = 0; i < s.length(); i++) { for (int j = 0; j <= i; j++) { String sub = s.substring(j, i + 1); if ((j == 0 || dp[j - 1]) && set.contains(sub)) { dp[i] = true; break; } } } return dp[s.length() - 1]; } }
|
https://chatgpt.com/s/t_68ea8e7831c48191ba35865a1392cdee
思路
- 可能性问题可以优先想到 DP
- 必会题,01背包
- dp[i][j] 表示[0..i]里若干个元素是否可以构成和为j

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| class Solution { public boolean canPartition(int[] nums) { int sum=0; for(int i=0;i<nums.length;i++) sum+=nums[i]; int avg =sum/2; if(sum % 2 == 1) return false; boolean [][]dp = new boolean[nums.length][avg + 1]; if(nums[0] <= avg){ dp[0][nums[0]] = true; } for(int i = 1; i < nums.length; i++){ for(int j = 0; j <= avg; j++){ if(nums[i] <= j){ dp[i][j] = dp[i - 1][j] || dp[i - 1][j - nums[i]]; } else dp[i][j] = dp[i - 1][j]; } if(dp[i][avg]) return true; } return dp[nums.length - 1][avg]; } }
|
压缩:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| class Solution { public boolean canPartition(int[] nums) { int sum=0; for(int i=0;i<nums.length;i++) sum+=nums[i]; int avg =sum/2; if(sum % 2 == 1) return false; boolean []dp = new boolean[avg + 1]; if(nums[0] <= avg) dp[nums[0]] = true; for(int i = 1; i < nums.length; i++){ for(int j = avg; j >= 0; j--){ if(nums[i] <= j) dp[j] = dp[j] || dp[j - nums[i]]; } if(dp[avg]) return true; } return dp[avg]; } }
|
分割成和最相近的两个子集
完全背包
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
| class Solution {
public int numSquares(int n) { if (n <= 3) return n; int length = n / 2; int[] nums = new int[length]; int count = 1; for (int i = 0; i < nums.length; i++, count++) { nums[i] = count * count; } int dp[] = new int[n + 1]; dp[0] = 0; for (int i = 1; i < dp.length; i++) dp[i] = Integer.MAX_VALUE; for (int i = 0; i < nums.length; i++) { for (int j = 0; j <= n; j++) { if (j >= nums[i] && dp[j - nums[i]] != Integer.MAX_VALUE) dp[j] = Math.min(dp[j], dp[j - nums[i]] + 1); } } return dp[n]; } }
|
思路
- 树形DP,树的每个节点存储两种状态(抢or不抢),然后从底递推到 根节点
- 当前节点抢,则 left不抢 + right不抢 + 1
- 若当前节点不抢,则左右节点的抢or不抢中取最大,然后相加。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
|
class Solution { public int rob(TreeNode root) { int[] res = dfs(root); return Math.max(res[0], res[1]); } public int[] dfs(TreeNode root){ if(root == null){ return new int[]{0,0}; } int []left = dfs(root.left); int []right = dfs(root.right); int rob = left[1] + right[1] + root.val; int noRob = Math.max(left[0], left[1]) + Math.max(right[0], right[1]); return new int[]{rob, noRob}; } }
|
思路
- 两个状态:持有or不持有
- dp数组含义 第i 持有or不持有股票 的最大利润
- 状态机DP
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| class Solution { public int maxProfit(int[] prices) { int[][] dp = new int[prices.length][2]; dp[0][0] = 0; dp[0][1] = -prices[0]; for (int i = 1; i < prices.length; i++) {
dp[i][0] = Math.max(dp[i - 1][0], dp[i - 1][1] + prices[i]);
if (i == 1) dp[i][1] = Math.max(dp[i - 1][1], dp[i - 1][0] - prices[i]); else dp[i][1] = Math.max(dp[i - 1][1], dp[i - 2][0] - prices[i]); } return dp[prices.length - 1][0]; } }
|
思路
- dp含义:第数为i,二进制后1的个数为 dp[i]
- 递推公式:当i为偶数,当i为奇数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
|
class Solution { public int[] countBits(int n) { int[] res = new int[n + 1]; if(n < 1) return res; res[0] = 0; res[1] = 1; for(int i = 2; i <= n; i++){ int num = i; if(num % 2 == 0){ res[i] = res[num / 2]; } else { res[i] = res[num / 2] + 1; }
} return res; } }
|