动态规划专题

一.leetcode

1.LeetCode403-青蛙过河

  • 题意
    一堆石头,给定一个数组stones,代表着石头的位置列表,青蛙在第一个石头上,第一个石头规定只能跳一个单位,青蛙上步跳k单位,那么规定下一步只能跳k-1,k,k+1单位,问青蛙是否可以跳到最后一个石头上(青蛙只能向前跳,不能向后跳)
  • 例子:
    • 输入:0,1,3,5,6,8,12,17
    • 输出:true
A.思考
  • 明显的一道动态规划的一道题,当然也可以用dfs来做,不过数组长度上限是4位数,不剪枝肯定会超时,所以这里我们用动态规划来做是最稳的
B.确定状态
  • 动态规划第一步就是想清楚开的数组的意义何在?开一维还是多维?所以我们可以先看最后一步,就以上面的输入为例子
  • 最后一个是17,青蛙可能可以从前一个石头跳过来,也可能可以从前前面的石头跳过来等等,反正就是从前面的石头跳过来…当然青蛙也存在跳不过来的可能性
  • 子问题:对于石头i来说,前面的石头j能否跳到石头i上面,需要知道这个石头j能否能跳跃(stones[i]-stones[j])这段距离,而想要知道石头j能否跳跃这段距离,又要知道石头j前面的石头u能否跳到石头j上面,需要知道这个石头u能否能跳跃(stones[j]-stones[u])这段距离…
  • 所以距离这个明显是个状态,不能忽视的…
C.转移方程
  • 设dp[i][j]为第i个石头能否跳跃j个单位
  • 对于石头i和石头j来讲,dist=stones[i]-stones[j],当dp[j][dist]为true时,dp[i][dist-1]=true,dp[i][dist]=true,dp[i][dist+1]=true
D.初始条件和边界情况
  • 第0个石头可以跳跃1个单位,也就是dp[0][1]=true
  • 因为你开的数组dp[i][j]的j是第i个石头能否跳j个单位,对于石头i来说,它最多能跳i+1个单位(比如第0个石头能跳1个单位,第1个石头最多能跳2个单位,第2个石头最多能跳3个单位…)
E.计算顺序
  • 从头到尾
F.代码
class Solution {
    public boolean canCross(int[] stones) {
        int len = stones.length;
		//代表的是第i个石头能否跳跃j个单位
		boolean [][] dp = new boolean[len][len+1];
		//第0个石头可以跳距离1
		dp[0][1]=true;
		for(int i=1;i<len;i++){
			for(int j=0;j<i;j++){
				int dist = stones[i]-stones[j];
				//给条件免得数组越界(对于石头j来说最多能跳j+1的距离)
				if(dist<=j+1&&dp[j][dist]){
					dp[i][dist-1]=true;
					dp[i][dist]=true;
					dp[i][dist+1]=true;
					//到达终点直接返回
                    if(stones[i]+dist-1==stones[len-1]||stones[i]+dist==stones[len-1]||stones[i]+dist+1==stones[len-1]){
                        return true;
                    }
				}

			}
		}

		return false;
    }
}

2.LeetCode1641-统计字典序元音字符串的数目

  • 题意
    返回长度为 n ,仅由元音 (a, e, i, o, u) 组成且按字典序排列的字符串数量
  • 例子
    • 输入:n = 1
    • 输出:5
A.确定状态

当n=1时,a,e,i,o,u 5个
当n=2时,aa,ae,ai,ao,au ;ee,ei,eo,eu;ii,io,iu;oo,ou;uu一共15个
观察n=2时的以a为前缀的正好是n=1的总数,以e为前缀的正好是n=1 e后的总数,以i为前缀正好是n=1 i后的总数,o和u也是同样的

B.转移方程
  • 设dp[n][i]是长度为n且前缀为i的以元音 (a, e, i, o, u) 组成且按字典序排列的字符串数量
  • dp[n][0]=dp[n-1][0]+dp[n-1][1]+…+dp[n-1][4];
  • dp[n][1]=dp[n-1][1]+dp[n-1][2]+…+dp[n-1][4];
  • dp[n][4]=dp[n-1][4];
C.初始条件和边界情况
  • 从1到n(含n),5个元音字母
  • dp[1][0]=dp[1][1]=dp[1][2]=dp[1][3]=dp[1][4]=1
D.计算顺序
  • 从头到尾
E.代码
class Solution {
    public int countVowelStrings(int n) {
        int [][] dp = new int[n+1][5];
        dp[1][0]=dp[1][1]=dp[1][2]=dp[1][3]=dp[1][4]=1;
        for(int i=2;i<=n;i++){
            for(int j=0;j<5;j++){
                for(int k=j;k<5;k++){
                    dp[i][j]+=dp[i-1][k];
                }
            }
        }
        return dp[n][0]+dp[n][1]+dp[n][2]+dp[n][3]+dp[n][4];
    }
}
F.优化代码
  • 其实这里可以直接开一个一维数组,便于理解我刚才才开的二维数组
  • dp[0]就是前缀为a的符合题意的字符串个数
class Solution {
    public int countVowelStrings(int n) {
        int [] dp = new int[5];
        dp[0]=dp[1]=dp[2]=dp[3]=dp[4]=1;
        for(int i=2;i<=n;i++){
            for(int j=0;j<5;j++){
                for(int k=j+1;k<5;k++){//j+1是不用去加自己
                    dp[j]+=dp[k];
                }
                
            }
        }
        return dp[0]+dp[1]+dp[2]+dp[3]+dp[4];
    }
}

3.LeetCode279-完全平方数

  • 题意

给定正整数 n,找到若干个完全平方数(比如 1, 4, 9, 16, …)使得它们的和等于 n。你需要让组成和的完全平方数的个数最少

  • 例子
    • 输入:n = 12
    • 输出:3(4+4+4)
A.确定状态
  • 这题和零钱兑换那题简直是如出一辙
  • 看最后一步,始终存在一个最少的个数,加上这个完全平方数i和就等于n,去掉这个完全平方数
  • 而我们想知道组成的和n所需要的最少的完全平方数数量,我们要先知道组成n-i*i这个数所需要的的最少的完全平方数的个数,所以子问题就来了
B.转移方程
  • 设dp[i]是组成和i所需最少的完全平方数的个数
  • dp[i]=min(dp[i],dp[i-j*j]+1)
C.初始条件和边界情况
  • 初始化dp[i]=i(相当于除开0这个数,全由1这个完全平方数加起来)
  • 从0到n(含n)
D.计算顺序
  • 从头到尾
E.代码
class Solution {
    public int numSquares(int n) {
           if(n==0){
               return 0;
           }
           int [] dp = new int [n+1];
           dp[0]=0;
           dp[1]=1;
           for(int i=2;i<=n;i++){
                  dp[i]=i;
                 for(int j=0;j*j<=i;j++){
                     if(i-j*j>=0){
                    	 dp[i]=Math.min(dp[i],dp[i-j*j]+1);
                     }     
                 }
           }
           return dp[n];
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值