斐波那契和台阶
刷多了leetcode就知道,斐波那契和青蛙上台阶是一个问题。
然后看了一篇博客,说动态规划最好打印出dp数组。拿斐波那契做一个例子
斐波那契
教科书级别的题目,f(n)=f(n-1)+f(n-2)
双层台阶
一般的台阶有两步。
因为上n层台阶时候,你可以上一步,也可以上两步达到n,所以取决于n-1和n-2的方法,这就是个斐波那契。
三步台阶
台阶还可以像这样上三步。
这里只放最通用的解法,状态之和前面三个状态有关,可以做进一步优化,这里不做优化
花费的台阶
这道题的题目讲的和屎一样。。。甚至有如下评论:
嗯。。相当文明,其实这个题目确实没有说清楚。。。因为假设有三个收费的楼梯[1,1,2],我们要去的地方其实是这样的,也就是说,终点其实比数组多了一格
事实上也好定义dp,dp【i】就是到第i个台阶时候花费的最小钱数。
dp[i] = min(dp[i-1],dp[i-2])咯,
我一开始写了这种乱七八糟的代码
class Solution {
public int minCostClimbingStairs(int[] cost) {
//dp=min[dp[(n-1)+dp(n-2)]] dp[i]表示:走到第i层阶梯的时候耗费的最小体力值
if(cost.length==1) return cost[0];
if(cost.length==2) return cost[1];
if(cost.length==3) return Math.min(cost[1] + cost[2],cost[0] + cost[2]);
int[] dp = new int[cost.length+1];
dp[0] = cost[0] ;
dp[1] = cost[1];
dp[2] = Math.min(cost[1] + cost[2],cost[0] + cost[2]);
for(int i=3;i<cost.length;i++){
System.out.print("比较 "+dp[i-1]+"+"+cost[i]+" 与 "+ dp[i-2]+" + "+cost[i]);
dp[i] = Math.min(dp[i-1]+cost[i], dp[i-2]+cost[i]);
}
dp[cost.length] = Math.min(dp[cost.length-1], dp[cost.length-2]);
System.out.println(" ");
for(int i:dp){
System.out.print(" "+i);
}
return dp[cost.length];
}
}
事实上推导出来以后哪里用得着这样啊
dp【i】=min(dp【i-1】,dp【i-2】),dp的数组长度比cost多一个,那么事实上初始化到dp[0]、dp[1]这两个就够了,dp【2】完全可以通过公式推导,优化一下代码
class Solution {
public int minCostClimbingStairs(int[] cost) {
//dp=min[dp[(n-1)+dp(n-2)]] dp[i]表示:走到第i层阶梯的时候耗费的最小体力值
if(cost.length<0) return 0;
if(cost.length<3) return cost[cost.length-1];
int[] dp = new int[cost.length+1];
dp[0] = cost[0] ;
dp[1] = cost[1];
for(int i=2;i<=cost.length;i++){
int tmp = i==cost.length?0:cost[i];
dp[i] = Math.min(dp[i-1], dp[i-2]) + tmp;
}
return dp[cost.length];
}
}
嗯,这样就简洁很多,当然,也可以进一步优化空间,这里不说那么细
矩阵路径
不同路径
这道题其实很好解答,首先机器人只能往右边或者往下边,那么在某一个点只和它左边和上边的那两个状态有关,如下图,绿色的点只能是往下走一步或者往右走一步得到。 dp[i][j] = dp[i-1][j] + dp[i][j-1];
那么考虑边界条件,两个选择单独做就是边界。。一直往右边或者一直往下边都是1。
然后看到矩阵,两个for循环写起来。。
for(int i=0;i<n;i++)
for(int j=0;i<m;j++)
…
最后的代码就是这样的:
class Solution {
public int uniquePaths(int m, int n) {
int[][] dp = new int[m][n];
for(int i=0;i<n;i++){
dp[0][i] = 1;
}
for(int i=0;i<m;i++){
dp[i][0] = 1;
}
for(int i=1;i<m;i++){
for(int j=1;j<n;j++){
dp[i][j] = dp[i-1][j] + dp[i][j-1];
}
}
return dp[m-1][n-1];
}
}
这道题目不难。
带障碍的不同路径
然后
那么还是同样的思路,定义dp数组开始
dp[m][n]表示到达坐标(m,n)这一格的时候方法的总数。
然后初始化这两排。要注意如果有石头那么石头后面的都是0
class Solution {
public int uniquePathsWithObstacles(int[][] obstacleGrid) {
int m = obstacleGrid.length;
int n = obstacleGrid[0].length;
if(obstacleGrid[0][0]==1){
return 0;
}
int[][] dp = new int[m][n];//定义为到达i,j这个点的方法数,如果为0就是到不了
dp[0][0] = 1;
//dp[0][1] = obstacleGrid[0][1] == 1?0:1;
//dp[1][0] = obstacleGrid[1][0] == 1?0:1;
//初始化两排
for(int i=1;i<m;i++){
if(obstacleGrid[i][0] ==1){
dp[i][0] = 0;
}else{
dp[i][0] = dp[i-1][0];
}
}
for(int i=1;i<n;i++){
if(obstacleGrid[0][i] ==1){
dp[0][i] = 0;
}else{
dp[0][i] = dp[0][i-1];
}
}
//开始dp
for(int i=1;i<m;i++){
for(int j=1;j<n;j++){
if(obstacleGrid[i][j]==1){
dp[i][j] = 0;
System.out.println("dp["+i+"]["+j+"]:0");
}else{
dp[i][j] = dp[i-1][j] + dp[i][j-1];
System.out.println("dp["+i+"]["+j+"]:"+dp[i-1][j] +"+"+dp[i][j-1]);
}
}
}
//打印出dp数组
for(int i=0;i<m;i++){
for(int j=0;j<n;j++){
System.out.print(" "+dp[i][j]);
}
System.out.println("");
}
return dp[m-1][n-1];
}
}
矩阵路径求和
求和其实也跟上面同样的道理,初始化只能往右边和下边的两列
然后持续dp:dp[i][j] = Math.min(dp[i-1][j],dp[i][j-1]) + grid[i][j];即可。
class Solution {
public int minPathSum(int[][] grid) {
int m = grid.length;
int n = grid[0].length;
int[][] dp = new int[m][n];
dp[0][0] = grid[0][0];
//初始化
for(int i=1;i<m;i++){
dp[i][0] = dp[i-1][0] + grid[i][0];
}
for(int i=1;i<n;i++){
dp[0][i] = dp[0][i-1] + grid[0][i];
}
//开始dp
for(int i=1;i<m;i++){
for(int j=1;j<n;j++){
dp[i][j] = Math.min(dp[i-1][j],dp[i][j-1]) + grid[i][j];
}
}
//打印dp
for(int i=0;i<m;i++){
for(int j=0;j<n;j++){
System.out.print(dp[i][j]+" ");
}
System.out.println(" ");
}
return dp[m-1][n-1];
}
}