算法学习动态规划-2

前言

紧跟上次学习,又多做几道题目加深印象理解

一、最长公共子序列

题目:
leetcode原题:最长公共子序列

分析:

这个属于样本模型,样本对应模型,一般关心最后一个的情况
题目是,给我们两个字符串,让我们求两个字符串最长公共子序列。我们可以将两个字符串转成两个字符数组,然后在递归种考虑最后一个字符情况
通过写递归然后改写成动态规划

递归代码:

public static int longestCommonSubsequence(String s1, String s2) {
        if (s1 == null || s2 == null || s1.length() == 0 || s2.length() == 0) {
            return 0;
        }

        char[] str1 = s1.toCharArray();
        char[] str2 = s2.toCharArray();
        return process(str1, str2, str1.length - 1, str2.length - 1);
    }

    /**
     * 返回str1[0...i]与str2[0..j]最长子序列
     *
     * @param str1
     * @param str2
     * @param i
     * @param j
     * @return
     */
    public static int process(char[] str1, char[] str2, int i, int j) {
        if (i == 0 && j == 0) {
            return str1[i] == str2[j] ? 1 : 0;
        } else if (i == 0) {
            if (str1[i] == str2[j]) {
                return 1;
            }
            return process(str1, str2, i, j - 1);
        } else if (j == 0) {
            if (str1[i] == str2[j]) {
                return 1;
            }
            return process(str1, str2, i - 1, j);
        } else {
            //i!=0 && j!=0
            int p1 = process(str1, str2, i - 1, j);
            int p2 = process(str1, str2, i, j - 1);
            int p3 = str1[i] == str2[j] ? (1 + process(str1, str2, i - 1, j - 1)) : 0;

            return Math.max(p1, Math.max(p2, p3));
        }
    }

动态规划后的代码

    public static int longestCommonSubsequence(String s1, String s2) {
        if (s1 == null || s2 == null || s1.length() == 0 || s2.length() == 0) {
            return 0;
        }

        char[] str1 = s1.toCharArray();
        char[] str2 = s2.toCharArray();
        int N = str1.length;
        int M = str2.length;
        int[][] dp = new int[N][M];
        dp[0][0] = str1[0] == str2[0] ? 1 : 0;

        for (int j = 1; j < M; j++) {
            dp[0][j] = str1[0] == str2[j] ? 1 : dp[0][j - 1];
        }

        for (int i = 1; i < N; i++) {
            dp[i][0] = str1[i] == str2[0] ? 1 : dp[i - 1][0];
        }
        for (int i = 1; i < N; i++) {
            for (int j = 1; j < M; j++) {
                int p1 = dp[i - 1][j];
                int p2 = dp[i][j - 1];
                int p3 = str1[i] == str2[j] ? (1 + dp[i - 1][j - 1]) : 0;
                dp[i][j] = Math.max(p1, Math.max(p2, p3));
            }
        }

        return dp[N - 1][M - 1];
    }

二、字符串最长回文

题目:
leectcode:字符串最长回文

分析:

这道题目可以转化成范围的模型,范围模型一般要考虑L..R,要考虑左右的关系
先改写成递归版本再改成动态规划

递归代码:

 //Input: s = "bbbab"
    //Output: 4
    //Explanation: One possible longest palindromic subsequence is "bbbb".
    public int longestPalindromeSubseq(String s) {
        if (s == null || s.length() == 0) {
            return 0;
        }
        char[] str = s.toCharArray();
        return process(str, 0, str.length - 1);
    }

    /**
     * 返回:L...R之间,最长回文子序列
     *
     * @param str
     * @param L
     * @param R
     * @return
     */
    public static int process(char[] str, int L, int R) {
        //base case
        if (L == R) {
            return 1;
        }
        //剩下两个字符
        if (L + 1 == R) {
            return str[L] == str[R] ? 2 : 1;
        }

        int p1 = process(str, L + 1, R);
        int p2 = process(str, L, R - 1);
        int p3 = process(str, L + 1, R - 1);
        int p4 = str[L] == str[R] ? (2 + process(str, L + 1, R - 1)) : 0;
        return Math.max(Math.max(p1, p2), Math.max(p3, p4));

    }

动态规划代码:

    public int longestPalindromeSubseq(String s) {
        if (s == null || s.length() == 0) {
            return 0;
        }
        char[] str = s.toCharArray();
        int N = str.length;
        int[][] dp = new int[N][N];
        
        //将两条对角线值先填写好
        dp[N - 1][N - 1] = 1;
        for (int i = 0; i < N - 1; i++) {
            dp[i][i] = 1;
            dp[i][i + 1] = str[i] == str[i + 1] ? 2 : 1;
        }

		//普遍情况
        for (int i = N - 3; i >= 0; i--) {
            for (int j = i + 2; j < N; j++) {
                int p1 = dp[i + 1][j];
                int p2 = dp[i][j - 1];
                int p3 = dp[i + 1][j - 1];
                int p4 = str[i] == str[j] ? (2 + dp[i + 1][j - 1]) : 0;
                dp[i][j] = Math.max(Math.max(p1, p2), Math.max(p3, p4));
            }
        }
        return dp[0][N - 1];
    }

三、象棋走日

题目:

请同学们自行搜索或者想象一个象棋的棋盘,
然后把整个棋盘放入第一象限,棋盘的最左下角是(0,0)位置
那么整个棋盘就是横坐标上9条线、纵坐标上10条线的区域
给你三个 参数 x,y,k
返回“马”从(0,0)位置出发,必须走k步
最后落在(x,y)上的方法数有多少种? 

分析:

马走日字,在普遍情况下,马在其中种间的话,对于下一步来讲共有8种跳法。
递归遍历马所有的走法,在走完最后一步时,判断是否在目标位置,在的话就找到一种步数。
递归写完再改写成动态规划

递归代码:


    public static int jump(int a, int b, int k) {
        return process(0, 0, a, b, k);
    }

    /**
     * 返回走完rest步,刚好在(a,b)位置共有多少种方法
     *
     * @param x    当前马 x位置
     * @param y    当前马 y位置
     * @param a    目标x位置
     * @param b    目标y位置
     * @param rest 剩余可以走的步数
     * @return
     */
    public static int process(int x, int y, int a, int b, int rest) {
        //越界情况
        if (x > 8 || x < 0 || y < 0 || y > 9) {
            return 0;
        }
        //base case
        if (rest == 0) {
            return x == a && y == b ? 1 : 0;
        }

        //正常情况下,可以选择的
        int ways = process(x + 1, y + 2, a, b, rest - 1);
        ways += process(x + 2, y + 1, a, b, rest - 1);
        ways += process(x + 2, y - 1, a, b, rest - 1);
        ways += process(x + 1, y - 2, a, b, rest - 1);
        ways += process(x - 1, y - 2, a, b, rest - 1);
        ways += process(x - 2, y - 1, a, b, rest - 1);
        ways += process(x - 2, y + 1, a, b, rest - 1);
        ways += process(x - 1, y + 2, a, b, rest - 1);

        return ways;
    }

动态规划代码:

public static int dp(int a, int b, int k) {
        int[][][] dp = new int[9][10][k + 1];

        dp[a][b][0] = 1;

        for (int rest = 1; rest <= k; rest++) {
            for (int x = 0; x < 9; x++) {
                for (int y = 0; y < 10; y++) {
                    //正常情况下,可以选择的
                    int ways = pick(dp, x + 1, y + 2, rest - 1);
                    ways += pick(dp, x + 2, y + 1, rest - 1);
                    ways += pick(dp, x + 2, y - 1, rest - 1);
                    ways += pick(dp, x + 1, y - 2, rest - 1);
                    ways += pick(dp, x - 1, y - 2, rest - 1);
                    ways += pick(dp, x - 2, y - 1, rest - 1);
                    ways += pick(dp, x - 2, y + 1, rest - 1);
                    ways += pick(dp, x - 1, y + 2, rest - 1);

                    dp[x][y][rest] = ways;

                }
            }
        }

        return dp[0][0][k];
    }

    public static int pick(int[][][] dp, int x, int y, int rest) {
        //越界情况
        if (x > 8 || x < 0 || y < 0 || y > 9) {
            return 0;
        }

        return dp[x][y][rest];
    }

总结

继续学习~~~不断敲,不断模仿,相信量变终将质变

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值