动态规划-1

前段时间面试遇到动态规划爬楼梯问题,当时用暴力枚举感觉面试官不是很满意,而且感觉解答不出来,现在将动态规划做个简单总结。

动态规划描述了一系列问题,这几种问题都可以通过动态规划求解,类似于高考时的几何题目,都可以通过坐标法求解出来,你要想求解这道几何题就要分为几个步骤,比如1.建立坐标2.证明........

动态规划也是同样如此,分为这几个步骤进行求解

首先我们要明确动态规划能解决什么问题

1.计数型问题

可以清楚的看到他问有多少种方法爬到楼顶,,明显可以看出是一个计数型问题。

2.最值问题

明显可以看到上述两个题中都涉及到了最大或者最小的硬币数量也要用动态规划求解

3.可行性问题

可以看到最后的问题是能否到达最后一个位置,这是可行性问题。

接下来我们看一下解决这类动态规划问题所需要的步骤

1.确定状态(确定状态分为两步   最后一步,子问题)

2.转移方程

3.初始条件和边界

我们将上述的几道题带入公式

1.爬楼梯问题

爬楼梯问题是无论如何都会爬楼梯所以要么选爬1节要么选2节

想一下最后一步是不是也是要么爬1要么爬2,再往前也是如此,所以就分解成了若干子问题

class Solution {

    public int climbStairs(int n) {

        if(n<=2){return n;}

        int [] opt = new int[n+1]; //定义状态例如opt[1]代表爬一节楼梯可能的方式,直到opt[n]代表爬n节楼梯可能的方式

        opt[1] =1; //1节楼梯只能是爬一节    1 

        opt[2] =2;//2节楼梯  方式有两种。 1+1   或者  2

        for(int i=3;i<=n;i++){

            opt[i] = opt[i-1]+opt[i-2];   //状态方程为如果爬一节 opt[i-1] 如果爬两节opt[i-2]将他们组合起来就是所有的情况

        }

        return  opt[n];

    }

}

爬楼梯问题得到了解答,问题重点在于得出 状态方程也就是当i>=3时,opt[i] = opt[i-1]+opt[i-2]

2.打家劫舍

打家劫舍代表着最值问题,举个栗子,数组A[1,2,3,1]代表每个房间的钱,数组下标为[0,1,2,3]

现在来想一下最后一步,最后一步是不是有两种情况我要么打劫,要么不打劫,那这个打劫的金额是不是也分为两种情况

打劫:opt[i-2]+A[i],表示我打劫这家得到的钱数A[i],因为不能连续打劫会出发报警,所以还要加上opt[i-2]代表打劫上一家获得的最大钱数

不打劫:opt[i-1],表示我不打劫这一家,打劫相邻家能获得的最高钱数

class Solution {

    public int rob(int[] nums) {

        int[] opt = new int[nums.length];//表示打劫到每家时获得的最大钱数

        if(nums.length>0){ //边界条件

             opt[0] = nums[0]; //表示在第一家打劫的最大钱数,为该家的钱数nums[0]

             if(nums.length>1){//边界条件

                 opt[1] = Math.max(nums[0],nums[1]); //表示在第二家打劫最大的钱数,只能在第一家或者第二家选一家最大的

             }            

            for(int i = 2;i<nums.length;i++){ //以下两种可能只能选一种,我们选钱最多的那一种。

            int a = nums[i]+opt[i-2];

            int b = opt[i-1];

            opt[i] = Math.max(a,b);

            }

        return opt[nums.length-1];//因为数组从0开始,注意边界条件。

        } 

        return 0;

    }

}

3.跳跃游戏

跳跃游戏的的最后一步这时你已经跳到了opt[j]这个位置上了,此时就要考虑如何才能跳到最后一步,那么这时候可以添加一个枚举i   0<=i<=j,表示如果有任意一个i在0到j中间能跳到j,同时满足nums[i]>=j-i则说明可以跳过去,就将这个问题分解为了子问题

class Solution {

    public boolean canJump(int[] nums) {

        boolean[] opt = new boolean[nums.length]; //定义一个数组表示能不能跳到第j个位置,因为是可行性问题所以注意定义

        opt[0] = true;//在初始位置默认是能跳到

        for(int j = 1;j<nums.length;j++){

            opt[j] = false; //开始默认跳不到

            for(int i = 0;i<j;i++){  //定义一个枚举

                if(opt[i] && nums[i]>=j-i){  //满足两个上述条件即可,说明在0<=i<=j中有个i能跳到j

                    opt[j] = true;

                    break;

                }

            }

        }

        return opt[nums.length-1];           

    }

}

上述题目均出自leetcode,动态规划这类题还是要多做,不然看了就会一写就废,记住公式,慢慢带入,主要还是要找到规律。

发布了12 篇原创文章 · 获赞 9 · 访问量 578

猜你喜欢

转载自blog.csdn.net/weixin_39475445/article/details/105076870
今日推荐