《Java刷题》动态规划练习题(一)

【题目1】最长递增子序列
给定数组arr,返回arr的最长递增子序列。

【举例】

arr=[2,1,5,3,6,4,8,9,7],返回的最长增长子序列为[1,3,4,8,9]。

public class Code01_LongthOfLIS {
    /**
     * @step1 明确dp含义,dp[i]表示以第i个元素结尾的最长增长子序列长度。
     * @step2 根据dp[0,...,i-1]->dp[i]
     * @ANS dp[i]初始值应该定为1
     */
    public static int lengthOfLIS(int[] nums) {
        int res = 0;
        // 定义dp数组
        int[] dp = new int[nums.length];
        // 定义base
        Arrays.fill(dp, 1);
        // dp[i] = Math.max(dp[i], dp[j] + 1);
        for (int i = 1; i < nums.length; i++) {
            int j = 0;
            while (j < i) {
                if (nums[j] < nums[i])
                    dp[i] = Math.max(dp[i], dp[j] + 1);
                j++;
            }
        }
        for (int i = 0; i < nums.length; i++) {
            res = Math.max(res, dp[i]);
        }
        return res;
    }

    public static void main(String[] args) {
        int[] nums = new int[] { 10, 9, 2, 5, 3, 7, 101, 18 };
        System.out.println(lengthOfLIS(nums));
    }
}

 【题目2】二维递增子序列

给定二维数组arr,返回arr的最长递增子序列长度。

【举例】

arr=[[5,4],[6,4],[6,7],[2,3]],返回的最长递增子序列长度为3。

public class Code2_MaxEnvelopes {
    public static int maxEnvelopes(int[][] envelopes) {
        int res = 0;
        // 按第一维升序,第二维降序进行排序。0和1代表第一和第二列。
        Arrays.sort(envelopes, new Comparator<int[]>() {
            public int compare(int[] lhs, int[] rhs) {
                return lhs[0] == rhs[0] ? rhs[1] - lhs[1] : lhs[0] - rhs[0];
            }
        });
        // 定义dp数组
        int len = envelopes.length;
        int[] dp = new int[len];
        Arrays.fill(dp, 1);
        // dp[i] = Math.max(dp[i], dp[j] + 1);
        for (int i = 1; i < len; i++) {
            int j = 0;
            while (j < i) {
                if (envelopes[j][1] < envelopes[i][1])
                    dp[i] = Math.max(dp[i], dp[j] + 1);
                j++;
            }
        }
        for (int i = 0; i < len; i++) {
            res = Math.max(res, dp[i]);
        }
        return res;
    }

    public static void main(String[] args) {

        int[][] envelpoes = new int[][] { { 5, 4 }, { 6, 4 }, { 6, 7 }, { 2, 3 }, { 10, 12 } };
        System.out.println(maxEnvelopes(envelpoes));

    }
}

 【题目3】最长公共子序列

给定两个字符串str1和str2,返回两个字符串的最长公共子序列。

【举例】

str1="1A2C3D4B56",str2="B1D23CA45B6A","123456"或者"12C4B6"都是最长公共子序列,返回哪个都行。

public class Code3_LongestCommonSubsequence {
    public static int longestCommonSubsequence(String str1, String str2) {
        int row = str1.length();
        int col = str2.length();
        char[] strArr1 = str1.toCharArray();
        char[] strArr2 = str2.toCharArray();
        // dp定义为二维数组,表示s1[0...i]和s2[0...j]中最长公共子序列的长度
        int[][] dp = new int[row + 1][col + 1];
        // 定义base case, dp[0][...]和dp[..][0]初始化为0
        for (int i = 0; i <= row; i++) {
            Arrays.fill(dp[i], 0);
        }
        for (int i = 0; i < row; i++) {
            for (int j = 0; j < col; j++) {
                if (strArr1[i] == strArr2[j])// 两个字符都在公共子序列中
                    dp[i + 1][j + 1] = dp[i][j] + 1;
                else {// 两个字符有1个在公共子序列中
                    dp[i + 1][j + 1] = Math.max(dp[i + 1][j], dp[i][j + 1]);
                }
            }
        }
        return dp[row][col];

    }

    public static void main(String[] args) {
        String str1 = "abcde";
        String str2 = "aceb";
        System.out.println(longestCommonSubsequence(str1, str2));
    }
}

 【题目4】最长公共子串

给定两个字符串str1和str2,返回两个字符串的最长公共子串。

【举例】

str1="1AB2345CD",str2="12345EF",返回"2345"。

public class Code4_LongestCommonSubstring {
    public static int longestCommonSubstring(String str1, String str2) {
        int row = str1.length();
        int col = str2.length();
        char[] strArr1 = str1.toCharArray();
        char[] strArr2 = str2.toCharArray();
        // dp定义为二维数组,表示s1[0...i]和s2[0...j]中以s1[i]和s2[j]结尾的最长公共子串的长度
        int[][] dp = new int[row + 1][col + 1];
        // 定义base case, dp[0][...]和dp[..][0]初始化为0
        for (int i = 0; i <= row; i++) {
            Arrays.fill(dp[i], 0);
        }
        for (int i = 0; i < row; i++) {
            for (int j = 0; j < col; j++) {
                if (strArr1[i] == strArr2[j])// 属于公共子串
                    dp[i + 1][j + 1] = dp[i][j] + 1;
                else {// 不属于公共子串,不同于最大公共子序列的地方!!
                    dp[i + 1][j + 1] = 0;
                }
            }
        }
        // 不同于最大公共子序列的地方!!
        int res = 0;
        for (int i = 0; i <= row; i++) {
            for (int j = 0; j <= col; j++) {
                res = Math.max(res, dp[i][j]);
            }
        }
        return res;

    }

    public static void main(String[] args) {
        String str1 = "fish";
        String str2 = "hish";
        System.out.println(longestCommonSubstring(str1, str2));
    }
}

【题目5】矩阵最小路径和
给定一个矩阵m,从左上角开始每次只能向右或者向下走,最后到达右下角位置,路径上所有的数字累加起来就是路径和,返回所有路径中最小的路径和。

【举例】

如果给定m如下:

1  3  5  9

8  1  3  4

5  0  6  1

8  8  4  0

路径1,3,1,0,6,1,0是所有路径中路径和最小的,返回12。

public class Code05_MinPathSum {
    public static int minPathSum(int[][] arr) {
        // 有效性判断
        if (arr == null || arr.length == 0 || arr[0] == null || arr[0].length == 0) {
            return 0;
        }
        int row = arr.length;
        int col = arr[0].length;
        int[][] dp = new int[row][col];
        // dp初始化
        dp[0][0] = arr[0][0];
        for (int i = 1; i < col; i++) {
            dp[0][i] = dp[0][i - 1] + arr[0][i];
        }
        for (int i = 1; i < row; i++) {
            dp[i][0] = dp[i - 1][0] + arr[i][0];
        }
        for (int i = 1; i < row; i++) {
            for (int j = 1; j < col; j++) {
                // 状态转移
                dp[i][j] = Math.min(dp[i - 1][j], dp[i][j - 1]) + arr[i][j];
            }
        }
        return dp[row - 1][col - 1];
    }

    public static void main(String[] args) {
        int[][] m = { { 1, 3, 5, 9 }, { 8, 1, 3, 4 }, { 5, 0, 6, 1 }, { 8, 8, 4, 0 } };
        System.out.println(minPathSum(m));
    }

}

 

posted @ 2021-10-20 21:40  云寄桑  阅读(158)  评论(0)    收藏  举报