动态规划经典题目
- 爬楼梯(70题,509斐波那契数相同)
class Solution {
public int climbStairs(int n) {
int[] dp=new int[n+1];
if (n==1||n==2){
return n;
}
dp[1]=1;dp[2]=2;
for (int i=3;i<=n;i++){
dp[i]=dp[i-1]+dp[i-2];
}
return dp[n];
}
}
- 使用最小花费爬楼梯(746题)
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];
}
}
- 打家劫舍(198题)
class Solution {
public int rob(int[] nums) {
if (nums.length==0){
return 0;
}
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]);
for (int i=2;i<nums.length;i++){
dp[i]=Math.max(dp[i-1],dp[i-2]+nums[i]);
}
return dp[nums.length-1];
}
}
- 打家劫舍2(213题)
class Solution {
public int rob(int[] nums) {
if (nums.length==0) return 0;
if (nums.length==1) return nums[0];
if (nums.length==2) return Math.max(nums[0],nums[1]);
return Math.max(rob(nums,0,nums.length-2),rob(nums,1,nums.length-1));
}
public int rob(int[] nums,int start,int end){
int[] dp=new int[nums.length];
dp[start]=nums[start];
dp[start+1]=Math.max(nums[start],nums[start+1]);
for (int i=start+2;i<=end;i++){
dp[i]=Math.max(dp[i-1],dp[i-2]+nums[i]);
}
return dp[end];
}
}
- 打家劫舍3(337题)
class Solution {
public int rob(TreeNode root) {
int[] res= dfs(root);
return Math.max(res[0],res[1]);
}
public int[] dfs(TreeNode node){
if (node==null) return new int[]{0,0};
int[] left = dfs(node.left);
int[] right = dfs(node.right);
int select = node.val + left[1] + right[1];
int unselect = Math.max(left[0],left[1])+Math.max(right[0],right[1]);
return new int[]{select,unselect};
}
}
- 最大子序和(53题)
class Solution {
public int maxSubArray(int[] nums) {
int[] dp=new int[nums.length];
int max=nums[0];
dp[0]=nums[0];
for (int i=1;i<nums.length;i++){
dp[i]=Math.max(dp[i-1]+nums[i],nums[i]);
if (dp[i]>max){
max=dp[i];
}
}
return max;
}
}
- 零钱兑换(322题)
class Solution {
public int coinChange(int[] coins, int amount) {
int[] dp=new int[amount+1];
Arrays.fill(dp,-1);
dp[0]=0;
for (int i=1;i<=amount;i++){
for(int coin:coins){
if (i-coin>=0 && dp[i-coin]!=-1){
if (dp[i]==-1||dp[i-coin]+1<dp[i]){
dp[i]=dp[i-coin]+1;
}
}
}
}
return dp[amount];
}
}
- 三角形最小路径和(120题)
class Solution {
public int minimumTotal(List<List<Integer>> triangle) {
int n=triangle.size();
int[][] dp=new int[n][n];
dp[0][0]=triangle.get(0).get(0);
for (int i=1;i<n;i++){
dp[i][0]=dp[i-1][0]+triangle.get(i).get(0);
for (int j=1;j<i;j++){
int ele=triangle.get(i).get(j);
dp[i][j]=ele+Math.min(dp[i-1][j-1],dp[i-1][j]);
}
dp[i][i]=dp[i-1][i-1]+triangle.get(i).get(i);
}
int min=dp[n-1][0];
for (int j=1;j<n;j++){
min=Math.min(min,dp[n-1][j]);
}
return min;
}
}
- 最长上升子序列(300题)
class Solution {
public int lengthOfLIS(int[] nums) {
if (nums.length == 0) {
return 0;
}
int[] dp=new int[nums.length];
dp[0]=1;
int maxCount=1;
for (int i=1;i<dp.length;i++){
int m=0;
for (int j=0;j<i;j++){
if (nums[j]<nums[i]){
m=Math.max(dp[j],m);
}
}
dp[i]=m+1;
maxCount=Math.max(maxCount,dp[i]);
}
return maxCount;
}
}
- 最小路径和(64题)
class Solution {
public int minPathSum(int[][] grid) {
int[][] dp=new int[grid.length][grid[0].length];
dp[0][0]=grid[0][0];
for (int i=1;i<grid[0].length;i++){
dp[0][i]=dp[0][i-1]+grid[0][i];
}
for (int j=1;j<grid.length;j++){
dp[j][0]=dp[j-1][0]+grid[j][0];
}
for(int i=1;i<grid.length;i++){
for(int j=1;j<grid[0].length;j++){
dp[i][j]=Math.min(dp[i-1][j],dp[i][j-1])+grid[i][j];
}
}
return dp[grid.length-1][grid[0].length-1];
}
}
- K站中转内最便宜的航班(787题)
class Solution {
public int findCheapestPrice(int n, int[][] flights, int src, int dst, int K) {
// dp[i][k]是经过k个中转站后到达站 i 的最小费用
int[][] dp = new int[n][K + 1];
// 循环初始化整个二维数组。
for(int i = 0; i < n; ++i) Arrays.fill(dp[i], Integer.MAX_VALUE);
// 利用flights中的信息初始化src可直达的班次
for(int[] flight : flights) {
if(flight[0] == src){
dp[flight[1]][0] = flight[2];
}
}
// 循环初始化数组中dst == src的行
for(int i = 0; i <= K; i++){
dp[src][i] = 0;
}
//动态规划状态转移方程,开始填表
//直达的已经初始化了(即k = 0的情况),现在从k = 1 的开始,即只有一个中转站开始
for(int k = 1; k <= K; k++){
for(int[] flight : flights){
//结合题目理解
if(dp[flight[0]][k - 1] != Integer.MAX_VALUE){
dp[flight[1]][k] = Math.min(dp[flight[1]][k], dp[flight[0]][k - 1] + flight[2]);
}
}
}
return dp[dst][K] == Integer.MAX_VALUE? -1: dp[dst][K];
}
}
- 二维区域和检索 - 矩阵不可变(304题)
class NumMatrix {
private int[][] dp;
public NumMatrix(int[][] matrix) {
if (matrix.length == 0 || matrix[0].length == 0) return;
dp = new int[matrix.length + 1][matrix[0].length + 1];
for (int r = 0; r < matrix.length; r++) {
for (int c = 0; c < matrix[0].length; c++) {
dp[r + 1][c + 1] = dp[r + 1][c] + dp[r][c + 1] + matrix[r][c] - dp[r][c];
}
}
}
public int sumRegion(int row1, int col1, int row2, int col2) {
return dp[row2+1][col2+1]-dp[row2+1][col1]-dp[row1][col2+1]+dp[row1][col1];
}
}
- 矩阵区域和(1314题)
class Solution {
public int[][] matrixBlockSum(int[][] mat, int K) {
int[][] dp =new int[mat.length+1][mat[0].length+1];
for(int i=0;i<mat.length;i++){
for (int j=0;j<mat[0].length;j++){
dp[i+1][j+1]=dp[i+1][j]+dp[i][j+1]+mat[i][j]-dp[i][j];
}
}
int[][] res=new int[mat.length][mat[0].length];
for(int i=0;i<mat.length;i++){
for (int j=0;j<mat[0].length;j++){
int topLeft_i=Math.max(i-K,0);
int topLeft_j=Math.max(j-K,0);
int bottomRight_i= Math.min(i + K, mat.length-1);
int bottomRight_j=Math.min(j + K, mat[0].length-1);
res[i][j]=dp[bottomRight_i+1][bottomRight_j+1]-dp[bottomRight_i+1][topLeft_j]-dp[topLeft_i][bottomRight_j+1]+dp[topLeft_i][topLeft_j];
}
}
return res;
}
}
- 不同路径(62题,63相似)
class Solution {
public int uniquePaths(int m, int n) {
int[][] dp=new int[m][n];
for (int i=0;i<m;i++){
dp[i][0]=1;
}
for (int j=0;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];
}
}
- 不同的二叉搜索树
class Solution {
public int numTrees(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++){
int index=0;
while (index<i){
dp[i]+=dp[index]*dp[i-1-index];
index++;
}
}
return dp[n];
}
}
- 最长公共子串
public class Solution {
public String LCS (String str1, String str2) {
if(str1.length() == 0 || str2.length() == 0){
return "-1";
}
char[] s1 = str1.toCharArray();
char[] s2 = str2.toCharArray();
int max = 0; //记录最大长度
int endIndex = -1; //记录最大子串末尾的下标
int[][] dp = new int[s1.length][s2.length];
for (int i = 0; i < s1.length; i++) {
for (int j = 0; j < s2.length; j++) {
if(i==0 || j==0) {
dp[i][j] = s1[i] == s2[j] ? 1: 0;
} else {
if(s1[i] == s2[j]) {
dp[i][j] = dp[i-1] [j-1] + 1;
}
}
if(dp[i][j] > max) {
max = dp[i][j];
endIndex = i;
}
}
}
return endIndex==-1 ? "-1" : str1.substring(endIndex-max+1, endIndex+1);
}
}
- 最长公共子序列(1143题,583题、1035题可以使用)
这里的dp[i][j]表示从字符串s1的位置i到字符串s2的位置j的之前最长的公共子序列长度
class Solution {
public int longestCommonSubsequence(String text1, String text2) {
int[][] dp=new int[text1.length()+1][text2.length()+1];
for (int i=1;i<=text1.length();i++){
for (int j=1;j<=text2.length();j++){
if (text1.charAt(i-1)==text2.charAt(j-1)){
dp[i][j]=dp[i-1][j-1]+1;
}else {
dp[i][j]=Math.max(dp[i-1][j],dp[i][j-1]);
}
}
}
return dp[text1.length()][text2.length()];
}
}
- 最长回文子串(516题)
dp[i][j]代表从i到j的最长回文子串
class Solution {
public int longestPalindromeSubseq(String s) {
int n = s.length();
int[][] dp=new int[s.length()][s.length()];
for (int i=n-1;i>=0;i--){
dp[i][i]=1;
for (int j=i+1;j<n;j++){
if (s.charAt(i)==s.charAt(j)){
dp[i][j] = dp[i+1][j-1]+2;
}else {
dp[i][j] = Math.max(dp[i+1][j],dp[i][j-1]);
}
}
}
return dp[0][n-1];
}
}
- 回文子串
dp[i][j]表示从i到j的字符串是否为回文字符串,当i和j字符相等时,分为三种情况,一种是i=j时一定是回文,一种是j=i+1时一定是回文,最后一种基于dp[i+1][j-1]来判断
class Solution {
public int countSubstrings(String s) {
int n = s.length();
boolean[][] dp=new boolean[s.length()][s.length()];
int ans = 0;
for (int i=n-1;i>=0;i--){
for (int j=i;j<n;j++){
if (s.charAt(i)==s.charAt(j) && (j - i <= 1 || dp[i + 1][j - 1])){
dp[i][j] = true;
ans++;
}
}
}
return ans;
}
}
- 编辑距离(72题)
这里的dp数组代表字符串s1的前i个字符和字符串s2的前j个字符的最小编辑距离,然后比较当前两个字母,如果相同则等于dp[i-1][j-1],不同则为dp[i-1][j]、dp[i][j-1]、dp[i-1][j-1]的最小值。分别代表从s1中插入
class Solution {
public int minDistance(String word1, String word2) {
int len1=word1.length();
int len2=word2.length();
int[][] dp=new int[len1+1][len2+1];
for(int i=1; i<=len1; i++){
dp[i][0] = i;
}
for(int i=1; i<=len2; i++){
dp[0][i] = i;
}
for(int i=1;i<=len1;i++){
char c = word1.charAt(i-1);
for(int j=1;j<=len2;j++){
if(c==word2.charAt(j-1)){
dp[i][j]=dp[i-1][j-1];
}else{
dp[i][j]=1+Math.min(Math.min(dp[i-1][j],dp[i][j-1]),dp[i-1][j-1]);
}
}
}
return dp[len1][len2];
}
}
- 不同的子序和(115题)
这里的dp表示字符串t的前j位在s的前i位中出现的次数
class Solution {
public int numDistinct(String s, String t) {
int m = s.length(),n=t.length();
if (m<n) return 0;
int[][] dp=new int[m+1][n+1];
for (int i=0;i<=m;i++){
dp[i][0]=1;
}
for (int i=1;i<=m;i++){
for (int j=1;j<=n;j++){
if (s.charAt(i-1)==t.charAt(j-1)){
dp[i][j]=dp[i-1][j-1]+dp[i-1][j];
}else {
dp[i][j] = dp[i-1][j];
}
}
}
return dp[m][n];
}
}
- 整数拆分(343题)
此题贪心算法更快
class Solution {
public int integerBreak(int n) {
int[] dp = new int[n + 1];
dp[2]=1;
for (int i = 3; i <= n; i++) {
for (int j = 1; j < i; j++) {
dp[i] = Math.max(dp[i], Math.max(j * (i - j), j * dp[i - j]));
}
}
return dp[n];
}
}
还在不断增加经典题目