样本对应模型例题

求两字符串的最长公共子序列

  • [[最长子序列]]
  • 子序列可以不连续,子串必须连续
  • 思路:样本对应模型先把数组对应成dp表,填basecase。找普遍的位置依赖
  • process(i,j)表示arr[i],buf[j]之前最长的公共子序列。此时可能以arr[i]结尾,以buf结尾,不以这两端结尾,同时以两端结尾

最短路径和问题

  • 题目:从二位数组在左上走到右下,只能向左,下走
  • 思路:从最下行的右往左填。每次选min(右,下)
  • 优化:因为每个位置依赖本行和下行->两行数组
  • 优化:因为每个位置依赖的本行右侧不回退,所以只用一行,右边直接覆盖即可
  •     package class21;
        
        public class Code01_MinPathSum {
        
        	public static int minPathSum1(int[][] m) {
        		if (m == null || m.length == 0 || m[0] == null || m[0].length == 0) {
        			return 0;
        		}
        		int row = m.length;
        		int col = m[0].length;
        		int[][] dp = new int[row][col];
        		dp[0][0] = m[0][0];
        		for (int i = 1; i < row; i++) {
        			dp[i][0] = dp[i - 1][0] + m[i][0];
        		}
        		for (int j = 1; j < col; j++) {
        			dp[0][j] = dp[0][j - 1] + m[0][j];
        		}
        		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]) + m[i][j];
        			}
        		}
        		return dp[row - 1][col - 1];
        	}
        
        	public static int minPathSum2(int[][] m) {
        		if (m == null || m.length == 0 || m[0] == null || m[0].length == 0) {
        			return 0;
        		}
        		int row = m.length;
        		int col = m[0].length;
        		int[] dp = new int[col];
        		dp[0] = m[0][0];
        		for (int j = 1; j < col; j++) {
        			dp[j] = dp[j - 1] + m[0][j];
        		}
        		for (int i = 1; i < row; i++) {
        			dp[0] += m[i][0];
        			for (int j = 1; j < col; j++) {
        				dp[j] = Math.min(dp[j - 1], dp[j]) + m[i][j];
        			}
        		}
        		return dp[col - 1];
        	}
        
        	// for test
        	public static int[][] generateRandomMatrix(int rowSize, int colSize) {
        		if (rowSize < 0 || colSize < 0) {
        			return null;
        		}
        		int[][] result = new int[rowSize][colSize];
        		for (int i = 0; i != result.length; i++) {
        			for (int j = 0; j != result[0].length; j++) {
        				result[i][j] = (int) (Math.random() * 100);
        			}
        		}
        		return result;
        	}
        
        	// for test
        	public static void printMatrix(int[][] matrix) {
        		for (int i = 0; i != matrix.length; i++) {
        			for (int j = 0; j != matrix[0].length; j++) {
        				System.out.print(matrix[i][j] + " ");
        			}
        			System.out.println();
        		}
        	}
        
        	public static void main(String[] args) {
        		int rowSize = 10;
        		int colSize = 10;
        		int[][] m = generateRandomMatrix(rowSize, colSize);
        		System.out.println(minPathSum1(m));
        		System.out.println(minPathSum2(m));
        
        	}
        }
        
    

K步超出矩阵概率

  • 题目:人在(row,col)走k步求超出[N][M]矩阵的概率。中途超出也算
  • 思路:当前位置直接调往四周走,K-1的情况。最后获得不超出的次数。总次数为4^k。
  • base case:只要当前坐标超出[N][M]直接返回-1,上层处理-1的情况
  • 传入:(row,col),K
  • 决策:往四周走
  • 传出:(row,col),K
  • dp:有三个参数需要改三维dp类似 [[象棋跳马问题]],k为层数
  • 预处理:0层的,[M][N]都填一
  • 普遍位置依赖 dpk-1层的四周位置
  • 代码
  •     public static double livePosibility2(int row, int col, int k, int N, int M) {
        		long[][][] dp = new long[N][M][k + 1];
        		for (int i = 0; i < N; i++) {
        			for (int j = 0; j < M; j++) {
        				dp[i][j][0] = 1;
        			}
        		}
        		for (int rest = 1; rest <= k; rest++) {
        			for (int r = 0; r < N; r++) {
        				for (int c = 0; c < M; c++) {
        					dp[r][c][rest] = pick(dp, N, M, r - 1, c, rest - 1);
        					dp[r][c][rest] += pick(dp, N, M, r + 1, c, rest - 1);
        					dp[r][c][rest] += pick(dp, N, M, r, c - 1, rest - 1);
        					dp[r][c][rest] += pick(dp, N, M, r, c + 1, rest - 1);
        				}
        			}
        		}
        		return (double) dp[row][col][k] / Math.pow(4, k);
        	}
    

人杀死怪兽的概率

  • 题目:人发动K次攻击,每次打击[0~M]等概率的血。怪的血量为N
  • 思路:每次攻击都枚举[0~M]攻击,往后传k–,M,n-相应的值。统计怪没死的次数。all=(M+1)^K
  • 传入:K,M,N
  • base case:N<=0 retuen 0,k=0&&N>0 return 1
  • 决策:对当前的K,M,N,枚举0~M的伤害值。接收每次调用的返回值,汇总后网上传
  • 传出:枚举行为调用相应的KMN
  • dp:这里有枚举行为所以要建严格的表结构处理依赖,只需要建立(K,N)的表
  • 预处理:(这里不好在格中填存活的次数,所以填死亡的次数),N=0列的死亡次数等于m^K
  • 普遍位置依赖:dp[k-1][n-0]~dp[k-1][n-m],(n-m>=1)

代码

     		  public static double dp2(int N, int M, int K) {
     		  		if (N < 1 || M < 1 || K < 1) {
     		  			return 0;
     		  		}
     		  		long all = (long) Math.pow(M + 1, K);
     		  		long[][] dp = new long[K + 1][N + 1];
     		  		dp[0][0] = 1;
     		  		for (int times = 1; times <= K; times++) {
     		  			dp[times][0] = (long) Math.pow(M + 1, times);
     		  			for (int hp = 1; hp <= N; hp++) {
     		  				dp[times][hp] = dp[times][hp - 1] + dp[times - 1][hp];
     		  				if (hp - 1 - M >= 0) {
     		  					dp[times][hp] -= dp[times - 1][hp - 1 - M];
     		  				} else {
     		  					dp[times][hp] -= Math.pow(M + 1, times - 1);
     		  				}
     		  			}
     		  		}
     		  		long kill = dp[K][N];
     		  		return (double) ((double) kill / (double) all);
     		  	}

给定一个正数求裂开的方法数

  • 题目:把一个正数才成其他正数,求组合
    • 给定一个正数1,裂开的方法有一种,(1) 给定一个正数2,裂开的方法有两种,(1和1)、(2) 给定一个正数3,裂开的方法有三种,(1、1、1)、(1、2)、(3) 给定一个正数4,裂开的方法有五种,(1、1、1、1)、(1、1、2)、(1、3)、(2、2)、 (4)
      给定一个正数n,求裂开的方法数。
  • 思路:两个参数,上一个裂出来的数pre,还剩的数rest。rest枚举所有减去pre的情况,因为相同值的算一个数(不用全排列),所以pre从小加到大
  • 传入:pre,rest
  • base case:rest-pre=0时 return 1;rest-pre<0 时 return 0;
  • 决策:枚举rest减去所有次数的pre的情况
  • 传出:newPre,newRest
  • 代码

    ```java
    		  public static int dp1(int n) {
    		  		if (n < 0) {
    		  			return 0;
    		  		}
    		  		if (n == 1) {
    		  			return 1;
    		  		}
    		  		int[][] dp = new int[n + 1][n + 1];
    		  		for (int pre = 1; pre <= n; pre++) {
    		  			dp[pre][0] = 1;
    		  			dp[pre][pre] = 1;
    		  		}
    		  		for (int pre = n - 1; pre >= 1; pre--) {
    		  			for (int rest = pre + 1; rest <= n; rest++) {
    		  				int ways = 0;
    		  				for (int first = pre; first <= rest; first++) {
    		  					ways += dp[first][rest - first];
    		  				}
    		  				dp[pre][rest] = ways;
    		  			}
    		  		}
    		  		return dp[1][n];
    		  	}
    ```
    
  • 存在枚举行为,所以可以用严格的表结构来优化(pre,rest)建表
  • 普遍位置的表依赖:一条斜线,找到能某个已经计算过部分斜线的格子
  • [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-uHf0JsYg-1663668124568)(…/assets/image_1641963812307_0.png)]
     public static int dp2(int n) {
     		if (n < 0) {
     			return 0;
     		}
     		if (n == 1) {
     			return 1;
     		}
     		int[][] dp = new int[n + 1][n + 1];
     		for (int pre = 1; pre <= n; pre++) {
     			dp[pre][0] = 1;
     			dp[pre][pre] = 1;
     		}
     		for (int pre = n - 1; pre >= 1; pre--) {
     			for (int rest = pre + 1; rest <= n; rest++) {
     				dp[pre][rest] = dp[pre + 1][rest];
     				dp[pre][rest] += dp[pre][rest - pre];
     			}
     		}
     		return dp[1][n];
     	}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值