动态规划做题总结

之前应该学的不是很牢固 做题有点不太会应用 所以就复习了一遍

然后先谈谈我对动态规划的理解吧 也是在借鉴了好几个文章之后 自己总结的 可能不是很好

但是应该比较便于刚开始接触的人理解

我们先看一个熟悉的题目-----------斐波那契数列

在之前学习递归时就已经对其掌握了

但是对其递归解法的更深一步思考之后 当我们需要求更多的fn值时

我们总会重复的计算f1 f2 f3.....

这也会大大的浪费时间 对此我们有了简单的解决方式 构造一个hash表 来记录 当我们再次需要某个值时 只需要 在hash中 直接拿出来利用

动态规划就是基于这样的一个思路 对其直接解决 

class Solution {
public:
    int fib(int n) {
        int MOD = 1000000007;
        if (n < 2) {
            return n;
        }
        int p = 0, q = 0, r = 1;
        for (int i = 2; i <= n; ++i) {
            p = q; 
            q = r; 
            r = (p + q)%MOD;
        }
        return r;
    }
};

通过代码也可以很显然的看出时间复杂度  为   O(n)

下面我们再看一个简单的题目来对此有更深的理解

最大子序和  

很简单的理解  最简单的暴力枚举求解  就不赘述了  我们直接用动态规划的思路 

首先 需要一个dp数组  那么这个dp数组代表的是什么 就尤为重要  更可以这么说 dp数组是动态规划的关键

那么对于本体的dp数组 代表的是什么 

显然当我们对于dp[n]而言  是n之前的最大子序和 

便于理解对于示例1  进行解释理解   dp数组如下

-21-2435615
012345678

对于 dp[0] 在num1数值中最大子序和为-2    dp[1] 有两种结果 -2+1  和  1  显然取最大的1

dp[2] 有两种 -3  和  -3+1  显然最大的 -2  

总结一下其动态转移方程为dp[i] = max(nums[i] , nums[i] + dp[i-1])

通过表格我们看到最大的为6

下面我们由上述的思路 进行实现如下

class Solution {
public:
    int maxSubArray(vector<int>& nums) {
        int *dp = new int [nums.size()];
        dp[0] = nums[0];
        int result = dp[0];
        for(int i=1;i<nums.size();i++){
            dp[i] = max(nums[i],nums[i]+dp[i-1]);
            if(dp[i]>result) result = dp[i];
        }
        return result;
    }
};

 对其优化 可以省去dp数组直接对其结果进行秩代取最大值即可

这里就不过多赘述

下面我们再来一个题目进行巩固

首先要考虑dp数组 思考过后我们可以发现

简单的一维数组不足解决  这里选择用二维数组记录

其中对于每一个数据而言需要有两个记录的东西

1.在当日持股即不卖出  所得利润

2.在当日不持股 卖出   所得利润

 得知道这两点之后我们对其列表观察

012345
j=0004455
j=1-7-1-1-1-1-1

 对于表格内容我们作出解释

当我们第一天时 若不持股 即则很显然为0  若持股 我们不妨不考虑其生活中的真实性 令为-7

第二天 若不持股则 在前一天不持股  和  前一天持股加今日的价格  取其最大值 显然为0

            若持股则  在前一天持股和 今日价格的负数  取其最大值为 -1

依次类推

显然状态转移方程为
        dp[i][0]=max(dp[i-1][0],dp[i-1][1]+prices[i]);
        dp[i][1]=max(dp[i-1][1],-prices[i]);

这里的表示还是需要好好自己理解的 

c++实现就如下所示了


    int len = prices.size();
    if(len<2) return 0;
    int (*dp)[2]= new int [len][2];
    dp[0][0]= 0;
    dp[0][1]= -prices[0];
    for(int i=1;i<len;++i){
        dp[i][0]=max(dp[i-1][0],dp[i-1][1]+prices[i]);
        dp[i][1]=max(dp[i-1][1],-prices[i]);
    }
    return dp[len-1][0];

那么 举了好几个例子之后动态规划最基本的实现方式也应该了解

对于这种类型的题目  显然有三种特点

1.最优子结构    

2.无后效性  :1.即推导某个状态时  其计算只关心前一个状态的结果   2.当某个状态确定时  和后面的状态无关

3.重复子问题

对于上诉的三个特点还需要多多做题才能更好的理解

当我们对其题目进行代码实现时 我们更需要关注的其实是 dp数组的表示或者说状态转移方程的实现

就对于刚刚的股票问题我们可以看到dp数组并不是那么的便于理解  

所以大部分的动态规划的难点都在于此

后续如果有更好的理解也会更新  

    cs200 zhoujie

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值