三维动态规划

文章介绍了四种使用动态规划解决的编程问题,涉及字符串操作(寻找最大组合、盈利计划、骑士概率和混淆字符串)、矩阵中和被K整除的路径计数。通过构建二维表格进行状态转移,优化空间并求解最优化问题。
摘要由CSDN通过智能技术生成

474. 一和零

public static int zeros, ones;
    public int findMaxForm(String[] strs, int m, int n) {
        /**
        空间压缩,创建一个二维表,
        最简单的状态就是第len层,值全是零
        二维表中每个格子的状态依赖于上一层的同一位置或是左边和下边的所有格子
        要求的是第0层的右上角格子
         */
        int[][] dp=new int[m+1][n+1];
        // 遍历字符串数组,每遍历一个字符串就更新一次dp表
        for(String str: strs){
            zerosAndOnes(str);
            // 每一层的dp表都是从右上角开始更新,即从右到左,从上到下
            // 注意,当满足下列条件时,才需要更新,否则,就依赖上一层的同一位置,无需更新
            for(int z = m; z>= zeros; z--){
                for(int o=n;o>=ones;o--){
                    dp[z][o]=Math.max(dp[z][o],1+dp[z-zeros][o-ones]);
                }
            }
        }
        return dp[m][n];
    }
    
    public static void zerosAndOnes(String str){
        zeros=0;
        ones=0;
        for(int i =0; i<str.length(); i++){
            if(str.charAt(i)=='0'){
                zeros++;
            }else{
                ones++;
            }
        }
    }

 879. 盈利计划

public static int mod = 1000000007;
    public int profitableSchemes(int n, int minProfit, int[] group, int[] profit) {
        /**
        空间压缩,二维dp,依赖于上一层的当前位置及左下角的格子
         */
        int[][] dp = new int[n+1][minProfit+1];
        // 当没有工作时,即i==group.length,
        // 这种情况下,当minProfit=0时,即计划+1,否则为0
        for(int r=0; r<=n; r++){
            dp[r][0]=1;
        }
        for(int i = group.length-1;i>=0;i--){
            for(int r = n;r>=0;r--){
                for(int s=minProfit;s>=0;s--){
                    // 第一种情况,不选择当前工作
                    int p1=dp[r][s];
                    // 选择做当前工作,
                    // Math.max(0,minProfit-profit[i])是为了保证dp表不超出索引,
                    // 因为剩余利润为0或是负数时,都表示该计划生效
                    int p2 = group[i]<=r ? dp[r-group[i]][Math.max(0,s-profit[i])] :0;
                    dp[r][s]=(p1+p2)%mod;
                }
            }
        }
        return dp[n][minProfit];
    }

 688. 骑士在棋盘上的概率

public double knightProbability(int n, int k, int row, int column) {
        double[][][] dp = new double[n][n][k+1];
        // 初始化dp表
        for(int i=0; i<n;i++){
            for(int j=0;j<n;j++){
                for(int t=0;t<=k;t++){
                    dp[i][j][t]=-1;
                }
            }
        }
        return f(n,row,column,k,dp);
    }

    public double f(int n, int i,int j,int k, double[][][] dp){
        // 如果越界
        if(i<0 || i>=n || j<0 || j>=n){
            return 0;
        }
        // 来到这儿就表示未越界
        if(dp[i][j][k] != -1){
            return dp[i][j][k];
        }
        double ans = 0;
        // 在未越界的情况下,判断是否停在了棋盘上
        if(k==0){
            return 1;
        }else{
            // 8个方向的概率均等
            ans += (f(n,i-2,j-1,k-1,dp)/8);
            ans+=(f(n,i-2,j+1,k-1,dp)/8);
            ans+=(f(n,i-1,j-2,k-1,dp)/8);
            ans+=(f(n,i-1,j+2,k-1,dp)/8);
            ans+=(f(n,i+1,j-2,k-1,dp)/8);
            ans+=(f(n,i+1,j+2,k-1,dp)/8);
            ans+=(f(n,i+2,j-1,k-1,dp)/8);
            ans+=(f(n,i+2,j+1,k-1,dp)/8);
        }
        dp[i][j][k]=ans;
        return ans;
    }

2435. 矩阵中和能被K整除的路径

public int numberOfPaths(int[][] grid, int k) {
        /**
        位置依赖版本,依赖于下边和右边的值
        看成一个n*m的二维表中,每个格子中有一个长度为k的数组
        要得到的是0,0,0,即左上角格子的余数为0的值
         */
        int n =grid.length;
        int m = grid[0].length;
        int[][][] dp = new int[n][m][k];
        // 先填上最简单的位置,即右下角的余数为r的值
        dp[n-1][m-1][grid[n-1][m-1] % k]=1;
        // 接下来填最右列的值
        for(int i = n-2; i>=0; i--){
            for(int r=0;r<k;r++){
                dp[i][m-1][r]=dp[i+1][m-1][(k+r-grid[i][m-1] % k)% k];
            }
        }
        // 填最下行的值
        for(int j=m-2;j>=0;j--){
            for(int r = 0;r<k;r++){
                dp[n-1][j][r]=dp[n-1][j+1][(k+r-grid[n-1][j] %k)%k];
            }
        }
        for(int i = n-2,need;i>=0;i--){
            for(int j = m-2;j>=0;j--){
                for(int r=0;r<k;r++){
                    // 后续需要凑出来的余数need
                    need = (k+r-grid[i][j]%k)%k;
                    dp[i][j][r]=dp[i+1][j][need];
                    dp[i][j][r]=(dp[i][j][r]+dp[i][j+1][need])%mod;
                }
            }
        }
        return dp[0][0][0];
    }

87. 扰乱字符串

 

 public boolean isScramble(String s1, String s2) {
        /**
        位置依赖版本,依赖于低层的格子
        dp[i][j][k]表示第一个字符串从i开始的k长度字符串对应于第二个字符串从j开始k长度
        要求的是dp[0][0][n]的值
         */
        char[] str1=s1.toCharArray();
        char[] str2=s2.toCharArray();
        int n = str1.length;
    
        boolean[][][] dp=new boolean[n][n][n+1];
        // 填写len=1层所有的格子
        for(int l1=0; l1<n; l1++){
            for(int l2=0;l2<n;l2++){
                dp[l1][l2][1]=str1[l1]==str2[l2];
            }
        }
        // 填写所有层
        // 注意边界条件,只要当l1<=n-len时才能执行
        for(int len=2;len<=n;len++){
            for(int l1=0;l1<=n-len;l1++){
                for(int l2=0;l2<=n-len;l2++){
                    // 不交错
                    for(int k=1;k<len;k++){
                        if(dp[l1][l2][k] && dp[l1+k][l2+k][len-k]){
                            dp[l1][l2][len]=true;
                            break;
                        }
                    }
                    // 交错
                    if(!dp[l1][l2][len]){
                        for(int i=l1+1,j=l2+len-1,k=1;k<len;i++,j--,k++){
                            if(dp[l1][j][k] && dp[i][l2][len-k]){
                                dp[l1][l2][len]=true;
                                break;
                            }
                        }
                    }
                }
            }
        }
        return dp[0][0][n];
    }

  • 3
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值