之前应该学的不是很牢固 做题有点不太会应用 所以就复习了一遍
然后先谈谈我对动态规划的理解吧 也是在借鉴了好几个文章之后 自己总结的 可能不是很好
但是应该比较便于刚开始接触的人理解
我们先看一个熟悉的题目-----------斐波那契数列
在之前学习递归时就已经对其掌握了
但是对其递归解法的更深一步思考之后 当我们需要求更多的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数组如下
-2 | 1 | -2 | 4 | 3 | 5 | 6 | 1 | 5 |
0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
对于 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.在当日不持股 卖出 所得利润
得知道这两点之后我们对其列表观察
0 | 1 | 2 | 3 | 4 | 5 | ||
j=0 | 0 | 0 | 4 | 4 | 5 | 5 | |
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