一.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];
}
}