【算法题-分类型总结】动态规划相关的算法题

1、跳台阶/楼梯

public class Solution {

    public int jumpFloor(int target) {

        int p = 1, q = 1;

        //和自下而上的方法使用相同的循环结构

        for(int i = 2; i <= target; i++) {

            int temp = p;

            p = p + q;

            q = temp;

        }

        return p;

    }

}

 

2、最长公共子串

import java.util.*;

 

 

public class Solution {

    /**

     * longest common substring

     * @param str1 string字符串 the string

     * @param str2 string字符串 the string

     * @return string字符串

     */

    public String LCS (String str1, String str2) {

        int len1 = str1.length(), len2 = str2.length();

        int[][] dp = new int[len1 + 1][len2 + 1];

        int max = 0;

        int index  = 0;

        for(int i = 0; i < len1; i++) {

            for(int j = 0; j < len2; j++) {

                if(str1.charAt(i) == str2.charAt(j)) {

                    dp[i + 1][j + 1] = dp[i][j] + 1;

                }

                if(dp[i + 1][j + 1] > max) {

                    max = dp[i + 1][j + 1];

                    index = i;

                }

            }

        }

        return str1.substring(index - max + 1, index + 1);

    }

}

 

3、最小代价爬楼梯

思路:每个台阶对应一个cost体力值,目标是花费最低,dp[i] = Math.min(dp[i - 1], dp[i - 2]) + cost[i]

class Solution {

    public int minCostClimbingStairs(int[] cost) {

        if (cost == null || cost.length == 0) {

            return 0;

        }

        if (cost.length == 1) {

            return cost[0];

        }

        int[] dp = new int[cost.length];

        dp[0] = cost[0];

        dp[1] = cost[1];

        for (int i = 2; i < cost.length; i++) {

            dp[i] = Math.min(dp[i - 1], dp[i - 2]) + cost[i];

        }

        //最后一步,如果是由倒数第二步爬,则最后一步的体力花费可以不用算

        return Math.min(dp[cost.length - 1], dp[cost.length - 2]);

    }

}

 

4、网格不同路径

方法1DFS回溯

class Solution {

private:

    int dfs(int i, int j, int m, int n) {

        if (i > m || j > n) return 0; // 越界了

        if (i == m && j == n) return 1; // 找到一种方法,相当于找到了叶子节点

        return dfs(i + 1, j, m, n) + dfs(i, j + 1, m, n);

    }

public:

    int uniquePaths(int m, int n) {

        return dfs(1, 1, m, n);

    }

};

 

方法2:动态规划

/**

     * 1. 确定dp数组下表含义 dp[i][j] 到每一个坐标可能的路径种类

     * 2. 递推公式 dp[i][j] = dp[i-1][j] dp[i][j-1]

     * 3. 初始化 dp[i][0]=1 dp[0][i]=1 初始化横竖就可

     * 4. 遍历顺序 一行一行遍历

     * 5. 推导结果 。。。。。。。。

     *

     * @param m

     * @param n

     * @return

     */

    public static 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 i = 0; i < n; i++) {

            dp[0][i] = 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];

    }

 

5、有障碍物不同路径

class Solution {

    public int uniquePathsWithObstacles(int[][] obstacleGrid) {

        int n = obstacleGrid.length, m = obstacleGrid[0].length;

        int[][] dp = new int[n][m];

        dp[0][0] = 1 - obstacleGrid[0][0];

        for (int i = 1; i < m; i++) {

            if (obstacleGrid[0][i] == 0 && dp[0][i - 1] == 1) {

                dp[0][i] = 1;

            }

        }

        for (int i = 1; i < n; i++) {

            if (obstacleGrid[i][0] == 0 && dp[i - 1][0] == 1) {

                dp[i][0] = 1;

            }

        }

        for (int i = 1; i < n; i++) {

            for (int j = 1; j < m; j++) {

                if (obstacleGrid[i][j] == 1) continue;

                dp[i][j] = dp[i - 1][j] + dp[i][j - 1];

            }

        }

        return dp[n - 1][m - 1];

    }

}

 

6、斐波那契数列

方法1:带备忘录的递归(自顶向下)

class Solution {

    //初始化备忘录为0

    int[] mome;

    public int fib(int n) {

        mome  = new int[n+1];

        if(n < 1) return 0;

        return comput(mome,n);

    }

    public int comput(int []mome,int n){

        if(n == 1 || n == 2) return 1;

        if(mome[n]!=0) return mome[n];

        mome[n] = comput(mome, n-1) + comput(mome, n-2);

        return mome[n];

    }

}

 

方法2:自底向上的dp数组

class Solution {

    public int fib(int n) {

        if(n < 1) return 0;

        if(n == 1 || n ==2) return 1;

        int[] arr = new int[n+1]; //Java默认将数组初始化为0

        arr[1] = arr[2] = 1;

        for(int i = 3; i <= n; i++){

            arr[i] =arr[i-1] +arr[i-2];

        }

        return arr[n];

    }

}

 

方法3:状态压缩

class Solution {

    public int fib(int n) {

        if(n < 1) return 0;

        if(n==1 || n==2) return 1;

        int prev=1,curr=1,sum;

        for(int i=3;i<=n;i++){

            sum = prev + curr;

            prev = curr;

            curr = sum;

        }

        return curr;

    }

}

 

7、凑零钱

含义:使用不同面值零钱凑出指定数值

思路:初始条件、状态、选择、dp数组

class Solution {

    public int coinChange(int[] coins, int amount) {

        if(amount < 0) return -1;

        int[] dp = new int[amount+1];

        Arrays.fill(dp,amount+1);

        dp[0] = 0;

        for(int i = 0; i < dp.length; i++){

            for(int coin:coins){

                if(i -coin < 0) continue;

                dp[i] = Math.min(dp[i],1+dp[i-coin]);

            }

        }

        return dp[amount] == amount + 1 ? - 1 : dp[amount];

    }

}

 

 

8、≤数的二进制中1的个数

思路:n个数,返回长度为n的数组

package com.iflytek;

 

import java.util.Arrays;

 

public class DP_bin_1 {

    public static void main(String[] args) {

        int[] count = testDp(5);

        //Arrays.asList(count).forEach(System.out::println); --只适用于对象类型的数组

        Arrays.stream(count).forEach(System.out::println);

    }

    public static int[] testIntegerBitCount(int n){

 

     int[] ans = new int[n+1];

        for (int i = 0; i <= n; i ++){

            ans[i] = Integer.bitCount(i);

        }

        return ans;

 

    }

    public static int[] testDp(int n){

        int[] dp = new int[n+1]; // java默认赋初值

        for (int i = 0; i < n/2; i++) {

            dp[2*i] = dp[i];

            if (2*i + 1 < n){

                dp[2*i+1] = dp[i] + 1;

            }

        }

        return dp;

    }

}

 

9、接雨水

思路:左右最小-当前值并累加

方法1:双指针

import java.util.*;

public class Solution {

    /**

     * max water

     * @param arr int整型一维数组 the array

     * @return long长整型

     */

    //双指针

    public long maxWater (int[] arr) {

        long res = 0;

        int left = 0, right = arr.length - 1;

        int leftMax = 0, rightMax = 0;

        while(left < right) {

            leftMax = Integer.max(leftMax, arr[left]);

            rightMax = Integer.max(rightMax, arr[right]);

            if(leftMax < rightMax) {

                res += leftMax - arr[left];

                left++;

            } else {

                res += rightMax - arr[right];

                right--;

            }

        }

        return res;

    }

}

 

方法2:动态规划

import java.util.*;

public class Solution {

    /**

     * max water

     * @param arr int整型一维数组 the array

     * @return long长整型

     */

    //双指针

    public long maxWater (int[] arr) {

        long res = 0;

        int n = arr.length;

        int[] leftMax = new int[n];

        leftMax[0] = arr[0];

        for(int i = 1; i < n; i++) {

            leftMax[i] = Math.max(leftMax[i - 1], arr[i]);

        }

        int[] rightMax = new int[n];

        rightMax[n - 1] = arr[n - 1];

        for(int i = n - 2; i >= 0; i--) {

            rightMax[i] = Math.max(rightMax[ i + 1], arr[i]);

        }

        for(int i = 0; i < n; i++) {

            res += Math.min(leftMax[i], rightMax[i]) - arr[i];

        }

        return res;

    }

}

 

10、最大子序和

含义:连续子数组的最大和,可以使用贪心/动态规划解决

// 动态规划

class Solution {

    public int maxSubArray(int[] nums) {

        int fi = 0, maxRes = nums[0]; //初始化

        for(int x : nums) {

            fi = Math.max(x, fi + x);

            maxRes = Math.max(maxRes, fi);

        }

        return maxRes;

    }

}

 

//贪心

class Solution {

    public int maxSubArray(int[] nums) {

        int maxRes = nums[0];

        int sum = 0;

        for(int x : nums) {

            if(sum > 0) {

                sum += x;

            } else {

                sum = x; // 等于x而不等于0

            }

            maxRes = Math.max(maxRes, sum);

        }

        return maxRes;

    }

}

 

11、最长上升子序列长度

思路:最长递增子序列(longest increasing subsequence),简称LIS,贪心/动态规划

方法1:贪心+二分

import java.util.*;

 

public class Solution {

    /**

     * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可

     * 给定数组的最长严格上升子序列的长度。

     * @param arr int整型一维数组 给定的数组

     * @return int整型

     */

    public int LIS (int[] arr) {

        int len = 1, n = arr.length;

        if(n == 0) {

            return 0;

        }

        int[] lisArr = new int[n + 1];

        lisArr[len] = arr[0];

        for(int i = 1; i < n; i++) {

            //len代表lisArr中的最后一个元素

            if(lisArr[len] < arr[i]) {

                lisArr[++len] = arr[i];

            } else {

                //二分,在lisArr数组中找比arr[i]小的,最靠后的位置pos

                //未找到,即lisArr数组中所有元素均大于arr[i]

                //需要更新lisArr数组的位置,为何不设为1呢?

                int l = 1,r = len, pos = 0;

                while(l <= r) {

                    //int mid = l + (r - l) >> 1;是错误的,原因是优先级导致的

                    int mid = l + ((r - l) >> 1);//位运算是为了避免溢出

                    //int mid = (l + r) >> 1;

                    if(lisArr[mid] < arr[i]) {

                        //更新指针

                        l = mid + 1;

                        pos = mid;

                    } else {

                        r = mid - 1;

                    }

                }

                //循环结束,找到了pos

                lisArr[pos + 1] = arr[i];

                //pos是最后一个位置,len不变

                //pos是中间位置,len仍不变,元素继续往后填

                //所以没有必要更新len位置

            }

        }

        return len;

    }

}

方法2:动态规划

import java.util.*;

 

public class Solution {

    /**

     * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可

     * 给定数组的最长严格上升子序列的长度。

     * @param arr int整型一维数组 给定的数组

     * @return int整型

     */

    public int LIS (int[] arr) {

        //动态规划实现

        //最长子序列应当使用max值记录

        int n = arr.length;

        if(n == 0) {

            return 0;

        }

        int[] dp = new int[n];

        Arrays.fill(dp, 1);

        int maxLen = Integer.MIN_VALUE;

        for(int i = 0; i < n; i++) {

            for(int j = 0; j < i; j++) {

                if(arr[j] < arr[i]) {

                    dp[i] = Math.max(dp[i], dp[j] + 1);

                }

                maxLen = Math.max(maxLen, dp[i]);

            }

        }

        return maxLen;

    }

}

 

12、最长上升子序列(三)

思路:动态规划+二分,避免超时

import java.util.*;

 

 

public class Solution {

    /**

     * retrun the longest increasing subsequence

     * @param arr int整型一维数组 the array

     * @return int整型一维数组

     */

    public int[] LIS (int[] arr) {

        int n = arr.length;

        int[] dp = new int[n];

        int[] tail = new int[n + 1];

        int end = 0;

        tail[0] = Integer.MIN_VALUE; //存储对应位置元素的值

        for(int i = 0; i < n; i++) {

            int num = arr[i];

            if(tail[end] < num) {

                end++;

                tail[end] = num;

                dp[i] = end;

            } else {

                int site = Arrays.binarySearch(tail, 1, end, arr[i]);

                if(site >= 0) {

                    continue;

                }

                int ins = -(site + 1);

                tail[ins] = arr[i];

                dp[i] = ins;

            }

        }

        int[] res = new int[end];

        int len = end;

        for(int i = n - 1; i >= 0; i--) {

            if(dp[i] == len) {

                res[len - 1] = arr[i];

                len--;

            }

        }

        return res;

    }

}

 

13、最长公共子序列的长度

思路:相等时为对角线+1,不相等时为左上最大值

class Solution {

    public int longestCommonSubsequence(String text1, String text2) {

        int len1= text1.length(), len2= text2.length();

        int[][] dp = new int[len1 + 1][len2 + 1];

        for(int i = 1; i <= len1; i++) {

            char c1 = text1.charAt(i - 1);

            for(int j = 1; j <= len2; j++) {

                char c2= text2.charAt(j - 1);

                if(c1 == c2) {

                    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[len1][len2];

    }

}

 

14、最长公共子序列

思路:知道长度后,倒着寻找

import java.util.*;

 

 

public class Solution {

    /**

     * longest common subsequence

     * @param s1 string字符串 the string

     * @param s2 string字符串 the string

     * @return string字符串

     */

    public String LCS (String s1, String s2) {

        int row = s1.length(), col = s2.length();

        int[][] dp = new int[row + 1][col + 1];

        for(int i = 1; i <= row; i++) {

            for(int j = 1; j <= col; j++) {

                if(s1.charAt(i - 1) == s2.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]);

                }

            }

        }

        if(dp[row][col] == 0) {

            return "-1";

        }

        //数组大小为公共子串的长度

        char[] lcs = new char[dp[row][col]];

        int cur = lcs.length - 1;

        while(true) {

            //什么时候把元素添加到结果中,应该倒序添加

            if(s1.charAt(row - 1) == s2.charAt(col - 1)) {

                lcs[cur--] = s1.charAt(row - 1);

                if(cur < 0) {

                    return new String(lcs);

                }

                row--;

                col--;

                

            } else {

                if(dp[row][col - 1] > dp[row - 1][col]) {

                    col--;

                } else {

                    row--;

                }

            }

        }

    }

}

 

15、编辑距离

思路:类似求公共子序列

//动态规划

//dp[len1][len2]

//初始化、值相等、不相等

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 = 0; i <= len1; i++) { //到len

            dp[i][0] = i;

        }

        for(int j = 0; j <= len2; j++) { //到len

            dp[0][j] = j;

        }

        for(int i = 1; i <= len1; i++) {

            for(int j = 1; j <= len2; j++) { //到len

                if(word1.charAt(i - 1) == word2.charAt(j - 1)) { //字符是-1

                    dp[i][j] = dp[i - 1][j - 1];

                } else {

                    dp[i][j] = 1 + Math.min(dp[i - 1][j], Math.min(dp[i][j - 1], dp[i - 1][j - 1]));

                }

            }

        }

        return dp[len1][len2];

    }

}

posted @ 2022-02-15 15:27  哥们要飞  阅读(52)  评论(0编辑  收藏  举报