算法与数据结构基础学习十二(从暴力递归到动态规划)

本文通过四个示例展示了暴力递归到动态规划的转换过程,包括机器人行走、纸牌游戏、背包问题和数字字符串转换问题。每个问题首先使用暴力递归解决,然后通过记忆化搜索实现动态规划优化,减少重复计算,提高效率。同时,探讨了动态规划的空间压缩、状态化简和四边形不等式等进一步优化技巧。

什么暴力递归可以优化?

在这里插入图片描述

暴力递归和动态规划的关系

在这里插入图片描述

如何找到某个问题的动态规划方式?

在这里插入图片描述

设计暴力递归过程的原则

在这里插入图片描述

四种尝试模型

在这里插入图片描述

暴力递归到动态规划的套路

在这里插入图片描述

动态规划的进一步优化

1)空间压缩
2)状态化简
3)四边形不等式
4)其他优化技巧

题目一

在这里插入图片描述

public class RobotWalk{
    public static int ways1(int N,int start,int aim,int K){
        if (N < 2 || start < 1 || start > N || aim < 1 || aim > N || K < 1){
            return -1;
        }
        return proccess1(start,K,aim,N);
    }

    // 机器人当前来到的位置是cur,
    // 机器人还有rest步需要去走,
    // 最终的目标是aim,
    // 有哪些位置?1~N
    // 返回:机器人从cur出发,走过rest步之后,最终停在aim的方法数,是多少?
    private static int proccess1(int cur,int rest,int aim,int n){
        if (rest == 0){
            return cur == aim ? 1:0;
        }
        if (cur == 1){
            return proccess1(cur+1,rest-1,aim,n);
        }
        if (cur == n){
            return proccess1(cur-1,rest-1,aim,n);
        }
        return proccess1(cur +1,rest-1,aim,n) + proccess1(cur-1,rest-1,aim,n);
    }
    public static int ways2(int N,int start,int aim,int K){
        if (N < 2 || start < 1 || start > N || aim < 1 || aim > N || K < 1){
            return -1;
        }
        int[][] dp = new int[N+1][K+1];
        for (int i = 0; i < N + 1; i++) {
            for (int j = 0; j < K + 1; j++) {
                dp[i][j] = -1;
            }
        }
        return proccess2(start,K,aim,N,dp);
    }
//记忆搜索法
    private static int proccess2(int cur,int rest,int aim,int n,int[][] dp){
        if (dp[cur][rest] != -1){
            return dp[cur][rest];
        }
        int ans = 0;
        if (rest == 0) {
            ans = cur == aim ? 1 : 0;
            dp[cur][rest] = ans;
            return ans;
        }
        if (cur == 1){
            ans = proccess2(cur+1,rest-1,aim,n,dp);
        }else if (cur == n){
            ans = proccess2(cur-1,rest-1,aim,n,dp);
        }else {
            ans = proccess2(cur-1,rest-1,aim,n,dp) + proccess2(cur+1,rest-1,aim,n,dp);
        }
        dp[cur][rest] = ans;
        return ans;
    }

    public static void main(String[] args){
        System.out.println(ways1(5,2,4,6));
        System.out.println(ways2(5,2,4,6));
    }
}

题目二

在这里插入图片描述

public class CardsInLine{
    //给定一个整型数组arr,代表数值不同的纸牌排成一条线
    //玩家A和玩家B依次拿走每张纸牌
    //规定玩家A先拿,玩家B后拿
    //但是每个玩家每次只能拿走最左或最右的纸牌
    //玩家A和玩家B都绝顶聪明
    //请返回最后获胜者的分数。
    public static int win1(int[] arr){
        if (arr == null || arr.length == 0){
            return 0;
        }
        int first = f1(arr,0,arr.length-1);
        int second = s1(arr,0,arr.length-1);
        return Math.max(first,second);
    }

    private static int s1(int[] arr,int L,int R){
        if (L == R){
            return arr[L];
        }
        int p1 = f1(arr,L+1,R);
        int p2 = f1(arr,L,R-1);
        return Math.min(p1,p2);
    }

    private static int f1(int[] arr,int L,int R){
        if (L == R){
            return arr[L];
        }
        int p1 = arr[L]+s1(arr,L+1,R);
        int p2 = arr[R] + s1(arr,L,R-1);
        return Math.max(p1,p2);
    }

    public static int win2(int[] arr){
        if (arr == null || arr.length == 0){
            return 0;
        }
        int N = arr.length;
        int[][] fmap = new int[N][N];
        int[][] gmap = new int[N][N];
        for (int i = 0; i < N; i++) {
            for (int j = 0; j < N; j++) {
                fmap[i][j] = -1;
                gmap[i][j] = -1;
            }
        }
        int first = f2(arr,0,arr.length-1,fmap,gmap);
        int second = g2(arr,0,arr.length-1,fmap,gmap);
        return Math.max(first,second);
    }

    private static int f2(int[] arr,int L,int R,int[][] fmap,int[][] gmap){
        if (fmap[L][R] != -1){
            return fmap[L][R];
        }
        int ans = 0;
        if (L == R){
            ans = arr[L];
        }else {
            int p1 = arr[L]+g2(arr,L+1,R,fmap,gmap);
            int p2 = arr[R] + g2(arr,L,R-1,fmap,gmap);
            ans = Math.max(p1,p2);
        }
        fmap[L][R] = ans;
        return ans;
    }

    private static int g2(int[] arr,int L,int R,int[][] fmap,int[][] gmap){
        if (gmap[L][R] != -1){
            return gmap[L][R];
        }
        int ans = 0;
        if (L != R){
            int p1 = f2(arr,L+1,R,fmap,gmap);
            int p2 = f2(arr,L,R-1,fmap,gmap);
            ans = Math.min(p1,p2);
        }
        gmap[L][R] = ans;
        return ans;
    }

    public static void main(String[] args){
        int[] arr = { 5, 7, 4, 5, 8, 1, 6, 0, 3, 4, 6, 1, 7 };
        System.out.println(win1(arr));
        System.out.println(win2(arr));
    }
}

题目三

在这里插入图片描述

public class Knapsack{
    // 所有的货,重量和价值,都在w和v数组里
    // 为了方便,其中没有负数
    // bag背包容量,不能超过这个载重
    // 返回:不超重的情况下,能够得到的最大价值
    public static int maxValue(int[] w,int[] v,int bag){
        if (w == null || w.length == 0 || v==null || v.length != w.length || bag == 0){
            return 0;
        }
        return proccess1(w,v,0,bag);
    }

    private static int proccess1(int[] w,int[] v,int index,int rest){
        if (rest<0){
            return -1;
        }
        if (index == w.length){
            return 0;
        }
        int ans = proccess1(w,v,index+1,rest-w[index]);
        if (ans != -1){
            ans += v[index];
        }
        int ans2 = proccess1(w,v,index+1,rest);
        return Math.max(ans,ans2);
    }
    public static int dp(int[] w,int[] v,int bag){
        if (w == null || w.length == 0 || v==null || v.length != w.length || bag == 0){
            return 0;
        }
        int N = w.length;
        int[][] dp = new int[N+1][bag+1];
        for(int index = N-1;index >=0; index--){
            for (int rest = 0; rest <= bag;rest++){
                int p1 = dp[index+1][rest];
                int p2 = 0;
                int next = rest - w[index] < 0 ? -1 :dp[index+1][rest-w[index]];
                if (next != -1){
                    p2 = next+v[index];
                }
                dp[index][rest] = Math.max(p1,p2);
            }
        }
        return dp[0][bag];
    }

    public static void main(String[] args){
        int[] weights = { 3, 2, 4, 7, 3, 1, 7 };
        int[] values = { 5, 6, 3, 19, 12, 4, 2 };
        int bag = 15;
        System.out.println(maxValue(weights, values, bag));
        System.out.println(dp(weights,values,bag));
    }

}

题目四

在这里插入图片描述

public class ConvertToLetterString{
    //规定1和A对应、2和B对应、3和C对应...26和Z对应
    //那么一个数字字符串比如"111”就可以转化为:
    //"AAA"、"KA"和"AK"
    //给定一个只有数字字符组成的字符串str,返回有多少种转化结果
    public static int number(String str){
        if (str == null || str.length() == 0){
            return 0;
        }
        return process(str.toCharArray(),0);
    }

    private static int process(char[] str,int i){
        if (str.length == i){
            return 1;
        }
        if (str[i] == '0'){ //没有0对应的字母,说明前一个决定有问题
            return 0;
        }
        int ways = process(str,i+1);
        if (i+1 < str.length && (str[i] - '0')*10 + (str[i+1] - '0') < 27){
            ways += process(str,i+2);
        }
        return ways;
    }
    // 从右往左的动态规划
    // 就是上面方法的动态规划版本
    // dp[i]表示:str[i...]有多少种转化方式
    public static int dp1(String s){
        if (s == null || s.length() == 0){
            return 0;
        }
        char[] str = s.toCharArray();
        int N = str.length;
        if (str[0] == '0'){
            return 0;
        }
        int[] dp = new int[N+1];
        dp[N] = 1;
        for (int i = N-1; i >=0 ; i--) {
            if (str[i] != '0') {
                int ways = dp[i + 1];
                if (i + 1 < str.length && (str[i] - '0') * 10 + (str[i + 1] - '0') < 27) {
                    ways +=dp[i+2];
                }
                dp[i] = ways;
            }
        }
        return dp[0];
    }
    // 从左往右的动态规划
    // dp[i]表示:str[0...i]有多少种转化方式
    public static int dp2(String s){
        if (s == null || s .length() == 0){
            return 0;
        }
        char[] str = s.toCharArray();
        int N = str.length;
        if (str[0] == '0'){
            return 0;
        }
        int[] dp = new int[N];
        dp[0] = 1;
        for (int i =1; i < N; i++){
            if (str[i] == '0'){
                if (str[i-1] == '0' || str[i-1] > '2' || (i-2 >=0 && dp[i-2] ==0)){
                    return 0;
                }else {
                    dp[i] = i-2 >= 0 ? dp[i-2] : 1;
                }
            }else {
                dp[i] = dp[i-1];
                if (str[i-1] != '0' && (str[i-1] - '0')*10 + (str[i] - '0') < 27){
                    dp[i] += i-2 >= 0 ? dp[i-2] : 1;
                }
            }
        }
        return dp[N-1];
    }
    // for test
    public static String randomString(int len) {
        char[] str = new char[len];
        for (int i = 0; i < len; i++) {
            str[i] = (char) ((int) (Math.random() * 10) + '0');
        }
        return String.valueOf(str);
    }
    //for test
    public static void main(String[] args) {
        int N = 30;
        int testTime = 1000000;
        System.out.println("测试开始");
        for (int i = 0; i < testTime; i++) {
            int len = (int) (Math.random() * N);
            String s = randomString(len);
            int ans0 = number(s);
            int ans1 = dp1(s);
            int ans2 = dp2(s);
            if (ans0 != ans1 || ans0 != ans2) {
                System.out.println(s);
                System.out.println(ans0);
                System.out.println(ans1);
                System.out.println(ans2);
                System.out.println("Oops!");
                break;
            }
        }
        System.out.println("测试结束");
    }
}

题目五

在这里插入图片描述

package com.msb.test12;

public class LongestCommonSubsequence{
    //给定两个字符串str1和str2,
    //返回这两个字符串的最长公共子序列长度
    //比如 : str1 = “a12b3c456d”,str2 = “1ef23ghi4j56k”
    //最长公共子序列是“123456”,所以返回长度6
    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);
    }

    private 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;
            }else {
                return process(str1,str2,i,j-1);
            }
        }else if (j == 0){
            if (str1[i] == str2[j]){
                return 1;
            }else {
                return process(str1,str2,i-1,j);
            }
        }else {
            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) :process(str1,str2,i-1,j-1);
            return Math.max(p1,Math.max(p2,p3));
        }
    }
    public static int dp1(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 M = str1.length;
        int N = str2.length;
        int[][] dp = new int[M][N];
        dp[0][0] = str1[0] == str2[0] ? 1 :0;
        for (int i = 1; i < M; i++) {
            if (str1[i] == str2[0]){
                dp[i][0] =1;
            }else {
                dp[i][0] = dp[i-1][0];
            }
        }
        for (int j = 1; j <N ; j++) {
            if (str1[0] == str2[j]){
                dp[0][j] = 1;
            }else {
                dp[0][j] = dp[0][j-1];
            }
        }
        for (int i = 1; i < M; i++) {
            for (int j = 1; j < N; j++) {
                dp[i][j] = str1[i] == str2[j] ? 1+dp[i-1][j-1] : Math.max(dp[i-1][j],dp[i][j-1]);
            }
        }
        return dp[M-1][N-1];
    }
}

----from左程云算法基础课

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值