DP动态规划

动态规划的基本性质:

1:问题具有最优子结构性质:问题所包含的子问题的解也是最有的。
2:无后效性:算一步是一步,只会存储当前的运算结果。为下一步计算提供结果。

动态规划的解题思路

核心:写出递归问题的转移方程并确定转移方程的边界条件。
爬楼梯问题
分析:对于爬上n阶楼梯的方法数,实际上就是爬上n-1阶和n-2阶方法数的总和(n-1再爬一阶就能到n,n-2再爬2阶就能到n)。对于边界条件就是爬1阶的方法数和爬2阶的方法数,然后根据转移方程不断推进,就可以得到答案。转移方程及边界条件
但动态规划问题并不都是这么直接,很多问题的答案案还有条件。
最小花费爬楼梯问题
分析:这个题在上述要求上,又增加了花费最小这个条件。我们求解的目标也从
方法数转移到了最小花费
这个问题上。由于我们可以选择起始位置(对于这两阶的花费都是0),然后对于登上第n阶的花费始终是登上n-1阶再加上该阶的花费与登上n-2阶再加上该阶的花费的最小值
问题的转移方程
目前,所遇到的更复杂的问题
最佳买卖股票时机含冷冻期
分析:这个问题由于冷冻期这一个规则的加入,使得一天出现了三种情况。1、持股 2、不持股且在冷冻期 3、不持股。对于这种多种情况的问题。虽然上面使用三个变量可以做,但为了好理解可以用二维数组来做。三列n行(n为天数)。
dp[i][0]表示第i天的持股情况,这个股票可以是继承i-1天时的,也可以是我们在非冷冻期当天买入的f[i][2]-prices[i]。由此可以得到转移方程取最大值
在这里插入图片描述
dp[i][1]表示i天不持股处于冷冻期。显然i-1天卖股了。可以得出动态方程
在这里插入图片描述
dp[i][2]表示天不持股且不处于冷冻期。可以是i-1天继承过来的 dp[i-1][2],也可以是前一天解冻。dp[i-1][1]。取最大值
在这里插入图片描述

边界条件:
在这里插入图片描述
剪绳子
分析:这道题与前面的问题的不同在于解题过程中使用了一个双重循环。递归方程中困难的地方在于不知道如何表示一条绳子剪多次的情况。其实可以这么考虑,二重循环中的j代表的是我们第一剪所剪下来的长度,此时剩余长度为i-j,这段长度所能拥有的最大乘积为dp[i-j]对于每一次变换第一剪的长度。我们总需要判断是否继续需要向下剪(我们不需要考虑剪几次,因为dp[i-j]中已经存储了相应的最大值)。判断当前不剪了与继续剪下去的结果,便能够得到题目的最终答案。
最低票价
分析:这个问题相对于前面的问题刚刚见到可能会感到无从下手,但显然是可以利用动态规划的思想去做的,由于票分为日票,周票,月票三种类型。递归方程中会出现如下的几种情况。为了方便推算递归方程,我们假设从这个月的第一天到数组中的最后一天我们一直都在旅行。这个是我一开始忽略条件而求解错误的情况。
第一种情况 当前的日期1<=i<7。这种情况下我们有两种选择甚至说三种选择,1 每一天都买日票。2 当到某一天后再买日票比周票贵了,我们选择去买一张周票。3 买着买着日票发现月票竟然比周票便宜(现实中几乎不可能发生,但给的数据里可能会有。),我们选择买一张月票。 我们可以写出如下的转移方程。dp[0]表示我们没有旅行计划时的花费为0。

if(i>=1 && i<=7){
    dp[i]=Math.min(dp[i-1]+costs[0],Math.min(dp[0]+costs[1],dp[0]+costs[2]));
}

第二种情况当前的日期 7<i<=30。这种情况下我们还是需要判断以上三种情况。只不过由于当前日期已经超过一周了。我们的转移方程需要稍微改变一下。

else if(i>7 && i<=30){
        dp[i]=Math.min(dp[i-7]+costs[1],Math.min(dp[i-1]+costs[0],dp[0]+costs[2]));

第三种情况当前的日期超过了30天来到了31天。转移方程如下

lse if(i>30){
         dp[i]=Math.min(dp[i-30]+costs[2],Math.min(dp[i-7]+costs[1],dp[i-1]+costs[0]));
         }

有了这个动态方程组,问题已经被解决了一大半。另一个问题就是所说的假设了,在这个假设下除了题目中数组给定我们的日期外,其余的日子我们仍然在旅行。所求出来的答案也不是题中旅行计划所得出的最小花费,而是从1号到计划中最后一天每天都在旅行的最小化费为此我们需要判断当前日期在不在我们旅行计划内,在的话我们才会通过动态方程组计算最小花费,否则它的花费应该与昨天相同!

for(int i=1;i<dp.length;i++){
                    if(i!=days[index]){
                            dp[i]=dp[i-1];
                    }
                    else{
                    if(i>=1 && i<=7){
                            dp[i]=Math.min(dp[i-1]+costs[0],Math.min(dp[0]+costs[1],dp[0]+costs[2]));
                    }
                    else if(i>7 && i<=30){
                            dp[i]=Math.min(dp[i-7]+costs[1],Math.min(dp[i-1]+costs[0],dp[0]+costs[2]));
                    }
                    else if(i>30){
                            dp[i]=Math.min(dp[i-30]+costs[2],Math.min(dp[i-7]+costs[1],dp[i-1]+costs[0]));
                    }
                    index++;
            }
            }
                            }

通过index来获取旅行计划的日期。如果是计算后 再获取下一个,反之与前一天花费相同。
01矩阵
分析:这个问题乍一看是个图,并且是最值的问题。应该考虑使用BFS的遍历方法解决。但仔细考虑,对于任意一个为1的矩阵,我们每次都可以从上,下,左,右四个方向来移动来求得最近0。对于一个1点,在我们知道了它只在 上左,上右,下左,下右四个移动方式分别移动到达最近的0的距离求最小值后,我们就知道了这个点最近0的距离。遍历时我们需要从dp矩阵的四个边角开始遍历。

class Solution {
    public int[][] updateMatrix(int[][] mat) {
            int n=mat.length;
            int m=mat[0].length;
            int dp[][]=new int[mat.length][mat[0].length];
            for(int i=0;i<dp.length;i++){
                    for(int j=0;j<dp[0].length;j++){
                            dp[i][j]=mat[i][j]==0?0:1000000;
                    }
            }
            //向左向下
            for(int i=n-1;i>=0;i--){
                    for(int j=0;j<m;j++){
                            if(i+1<n){
                                    dp[i][j]=Math.min(dp[i+1][j]+1,dp[i][j]);
                            }
                            if(j-1>=0){
                                    dp[i][j]=Math.min(dp[i][j-1]+1,dp[i][j]);
                            }
                    }
            }
            //向左 向上移动
            for(int i=0;i<n;i++){
                    for(int j=0;j<m;j++){
                            if(i-1>=0){
                                    dp[i][j]=Math.min(dp[i-1][j]+1,dp[i][j]);
                            }
                            if(j-1>=0){
                                    dp[i][j]=Math.min(dp[i][j-1]+1,dp[i][j]);
                            }
                    }
            }
             //向右向上移動
            for(int i=0;i<n;i++){
                    for(int j=m-1;j>=0;j--){
                            if(i-1>=0){
                                    dp[i][j]=Math.min(dp[i-1][j]+1,dp[i][j]);
                            }
                            if(j+1<m){
                                    dp[i][j]=Math.min(dp[i][j+1]+1,dp[i][j]);
                            }
                    }
            }
            //向右 向下移动
            for(int i=n-1;i>=0;i--){
                    for(int j=m-1;j>=0;j--){
                            if(i<n-1){
                                    dp[i][j]=Math.min(dp[i+1][j]+1,dp[i][j]);
                            }
                            if(j<m-1){
                                    dp[i][j]=Math.min(dp[i][j+1]+1,dp[i][j]);
                            }
                    }
            }
            return dp;
    } 
}

这个还可以进行简便计算,我们可以选取任意一组对角进行计算即可以得到结果。在一个次遍历中 我们得到了一个点 只在左上方向上移动距离的最近0的距离,第二次遍历我们又可以得到这个点只在右下方向上移动距离0最近的距离。这样包含了全部的四个方向,最终得到的结果也是最小的。

class Solution {
    public int[][] updateMatrix(int[][] mat) {
            int n=mat.length;
            int m=mat[0].length;
            int dp[][]=new int[mat.length][mat[0].length];
            for(int i=0;i<dp.length;i++){
                    for(int j=0;j<dp[0].length;j++){
                            dp[i][j]=mat[i][j]==0?0:1000000;
                    }
            }
            //向左 向上移动
            for(int i=0;i<n;i++){
                    for(int j=0;j<m;j++){
                            if(i-1>=0){
                                    dp[i][j]=Math.min(dp[i-1][j]+1,dp[i][j]);
                            }
                            if(j-1>=0){
                                    dp[i][j]=Math.min(dp[i][j-1]+1,dp[i][j]);
                            }
                    }
            }
            //向右 向下移动
            for(int i=n-1;i>=0;i--){
                    for(int j=m-1;j>=0;j--){
                            if(i<n-1){
                                    dp[i][j]=Math.min(dp[i+1][j]+1,dp[i][j]);
                            }
                            if(j<m-1){
                                    dp[i][j]=Math.min(dp[i][j+1]+1,dp[i][j]);
                            }
                    }
            }
            return dp;
    } 
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值