[学习记录]Dynamic Programming从入门到入土

[学习记录]Dynamic Programming从入门到入土

是的没错,这个文章能让你彻底学会:

dynamic programming 动态规划

悄悄话:是的,学会动规,至于精通请前往👉Dynamic Programming从入土到复活

Content

1. 从乘方和谚语提及

2. 斐波那契数列:递归受到了挑战

3. 动态规划的原理

--3.1最优子结构

--3.2重叠子问题

4.动态规划的应用

--4.1背包模型

--4.1.1 零一背包
--4.1.2 彻底背包
--4.1.3 多重背包

--4.2线性动规

--4.3区间动规

5.入土

提示:若是你对动规已经有所了解,请直接进入3或4


1.从乘方和谚语提及

相信大家都学习过乘方,那么咱们知道:

2 X 2 X 2 X 2 X 2 X 2 X 2 X 2 X 2 X 2 =2^10=1024

那么2^11是多少呢?你会天然而然地答出2048!

好的,祝贺你已经学会了动规初步!

为何?

咱们做为人,计算2^11天然不是2X2……X2=2048这样算的

而是经过已经给出的2^10=1024计算的:2X1024=2048。

这个过程就包括了动规的核心思想之一:

记住子问题的解。

一句谚语解释:

Those who cannot remember the past, are doomed to repeat it.--George Santayana
那些忘记过去的人注定要重蹈覆辙。

在编程中,动态规划经过记住子问题的解,可以以高效率低耗时而脱颖而出,成为最热门的算法之一(详见2)。

动规的概念能够自行百度,在这里不作过多讨论。


2. 斐波那契数列:递归受到了挑战

对于斐波那契数列的求解,递归每每是最早考虑的解法,可是在1000ms的苛求下,递归O(2^n)的时间复杂度就比较麻烦了。

//递归解斐波那契 long long Fib(long long n) { if (n==1||n==2) return 1; else return Fib(n - 1) + Fib(n - 2); }

图解👇

从图中能够看到,光是算fib(6),就要从新算3遍fib(3),时间复杂度就比较恶心了。对了,你想起什么没有?记住子问题的答案--动规登场!

1.子问题记忆法

使用一个数组来记录各个子问题的解,当再一次遇到这一问题的时候直接查找数组来得到解避免屡次计算子问题。
//动规解决斐波那契(子问题记忆法) int fib(int a[],int n) { if (n == 0) { a[0] = 0; return 0; } if (n == 1) { a[1] = 1; return 1; } if (a[n] >= 0) { return a[n]; } a[n] = fib(a, n - 1) + fib(a, n - 2); return fib(a, n - 1) + fib(a, n - 2); }

二、自底向上解决方案

先求解子问题再根据子问题的解来求解父问题,斐波那契数列的子问题图以下:
//动规解决斐波那契(自底向上解决) int fib(int a[],int n) { a[0] = 0; a[1] = 1; for (int i = 2; i <= n; i++) { a[i] = a[i - 1] + a[i - 2]; } return a[n]; }

自底向上的计算方法实现起来很是容易,分析算法,仅从形式上面分析算法可知,算法的时间主要消耗在计算数据规模为n的数组里面的数上面了,因此时间复杂度仅为:O(n)。递归在斐波那契的地位不保了!!

可是咱们能够看到,在使用动规的时候,咱们彷佛多开了一个数组,那么空间是否会受到影响?答案是确定的!可是在1000ms,256Mb的条件下,牺牲空间换取时间绝对是一笔很值的交♂易!

练习:LuoGu3986&钢条切割


3.动态规划的原理

最优子结构

用动态规划求解最优化问题的第一步就是刻画最优解的结构,若是一个问题的解结构包含其子问题的最优解,就称此问题具备最优子结构性质。所以,某个问题是否适合应用动态规划算法,它是否具备最优子结构性质是一个很好的线索。使用动态规划算法时,用子问题的最优解来构造原问题的最优解。所以必须考查最优解中用到的全部子问题。

重叠子问题

在斐波拉契和钢条切割中,能够看到大量的重叠子问题,好比说在求fib(6)的时候,fib(2)被调用了5次。若是使用递归算法的时候会反复求解相同的子问题,不停的调用函数,而不是生成新的子问题。若是递归算法反复求解相同的子问题,就称为具备重叠子问题性质。在动态规划算法中使用数组来保存子问题的解,问题屡次求解的时候能够直接查表不用调用函数递归。

简单来讲,动规就是递归再加上记录。

花小部分空间,省大部分时间。


4.动态规划的应用

4.1背包问题

1.零一背包

有N件物品和一个容量为V的背包。第i件物品的体积是w[i],价值是c[i]。求解将哪些物品装入背包可以使这些物品的费用总和不超过背包容量,每一个物品只能选用一次,使得价值总和最大。

这是\color{blue}\text{最基础}最基础的背包问题,总的来讲就是:选仍是不选

例题:LuoGu1048

显然,这道题并不能用贪心,由于假设你要在100时间内采药,有三个药:71时间,89价值;30时间,44价值;40时间,45价值。若是是贪心,就会果断选(71,89),但事实上(30,44)和(40,45)更优。此时请出动规,怎么用呢?

列出状态转移方程!

状态转移方程的概念能够自行百度,在这里不作过多讨论。

对于一个物品,只有两种状况

1.第i件不放进去,这时所得价值为:dp[i-1][v]

2.第i件放进去,这时所得价值为:dp[i-1][v-c[i]]+w[i]

故状态转移方程为:

dp[i][v] = max(dp[i-1][v], dp[i-1][v-w[i]]+c[i])
注:零一背包能够用一维数组记录最优计划dp[v],表示不超过v体积的最大价值。

巨佬们看过来!!背包退火👈了解一下?

2.彻底背包

有N种物品和一个容量为V的背包,每种物品都有无限件可用。第i种物品的体积是w[i],价值是c[i]。求解将哪些物品装入背包可以使这些物品的费用总和不超过背包容量,且价值总和最大。

彻底背包和01背包十分相像, 区别就是彻底背包物品有无限件。总的来讲就是选仍是不选,选几件。

例题:LuoGu1616

列出状态转移方程!

若不采这种草药,则时间花费没有增多,通过的 草药种数增长了1,采到草药价格不变,因此 dp[i][j]=dp[i-1][j];

若采这种草药,则 时间花费增长了t[i],种数增长1,采到草药价格增长了p[i],因此 dp[i][j]=dp[i-1][j-t[i]]+p[i]。

咱们要使dp[i][j]\color{red}\text{尽量大}尽量大,即👇

dp[i][j]=max(dp[i-1][j],f[i-1][j-t[i]]+p[i])

这道题对于药就没有限制了,每种都无限多,因此地主家的傻孩子优化出现了:

当一个药品的价值小于另外一个药品的价值,而且时间高于另外一个药品,咱们就能够不去考虑这个药品。既然咱们不是地主家的傻孩子,为何还要花更多的时间采更少价值的药呢😂?

注:该问题也能够压缩到一维最优计划dp[v],咱们能够将药的种类i省略 ,用后面算的值覆盖掉前面算的值.

巨佬们,背包退火👈了解一下?

3.多重背包

有N种物品和一个容量为V的背包。第i种物品有n[i]个可用,每件费用是w[i],价值是c[i]。求解将哪些物品装入背包可以使这些物品的费用总和不超过背包容量,且价值总和最大。

这里又多了一个限制条件,每一个物品规定了可用的次数。

例题:LuoGu2347

我才不会告诉你我如今仍是把多重背包拆成01背包来作的呢

没错,对于通常的多重背包,换成01背包水就好了,至于不通常的嘛……

列出状态转移方程!

f[i][v]=max(f[i-1][v-k*w[i]]+ k*c[i]|0<=k<=n[i])

4.2线性动规

其实线性动规是动规中较简单的一类题目,重点有三:

1.选对算法,正确维护

例题:LuoGu1115

2.巧妙利用前缀和

例题:LuoGu1387

3.别忘了合理递推

例题:LuoGu1052

线性递归的题我的以为要多练,因此放了几道题,但愿在刷题的过程当中,你可以领悟其精髓。同时,你能够参考一个近乎万能的线性模板博客👉线性动态规划


4.3区间动规

例题:LuoGu1435

典型的区间模型,回文串拥有很明显的子结构特征,即当字符串s是一个回文串时,在X两边各添加一个字符'a'后,a s a仍然是一个回文串,咱们用d[i][j]来表示A[i…j]这个子串变成回文串所须要添加的最少的字符数,那么对于A[i] == A[j]的状况,很明显有

d[i][j] = d[i+1][j-1]

这里须要明确一点,当i+1 > j-1时也是有意义的,它表明的是空串,空串也是一个回文串,因此这种状况下d[i+1][j-1] = 0;

当A[i] != A[j]时,咱们将它变成更小的子问题求解,咱们有两种决策:

1.在A[j]后面添加一个字符A[i];

2.在A[i]前面添加一个字符A[j];

列出状态转移方程!

d[i][j] = min{ d[i+1][j], d[i][j-1] } + 1; //每次状态转移,区间长度增长1
注:此算法空间复杂度O(n^2),时间复杂度O(n^2),优化请访问👉Dynamic Programming从入土到复活

撒花✿✿ヽ(°▽°)ノ✿

诶Σ( ° △ °|||)好像忘了点什么?


5.入土

如题所述,动态规划在时间上远胜递归,在通用性上远胜贪心,可是它毕竟只是个算法,不是自动AC机,接下来咱们简单的谈谈它的缺点:

1.它节省时间的代价是浪费空间

这一点算是比较弱的缺点了,由于它有时开的空间只是九牛一毛,不值一提。可是!!在解决二维地图题中,要开一个dp[10000][10000]就会比较吃力。

2.它须要具体问题具体分析

不像贪心或者递归,dp是基本不具备模板的(最长单调子序列模板别吵吵),并且在打dp以前,要仔细思考,由于号称具备全局最优解的动规有可能让你失去人生中美好的1.5小时。

3.它的状态转移方程须要考虑

再确认要用dp一头磕死在一道题上以后,必需要作的就是确认子问题之间的关系,而且由题意得出找出全局最优解的状态转移方程,若是找不出来,那么就应该及时更换方法。

4.dp并非不须要优化

向其余的大部分算法同样,dp也是须要优化的,可是优化每每又让人无从下手,搜索能够剪枝,但dp就不必定行了。好比上面的疯狂采药问题,咱们才能就提论题地进行优化。那么dp有没有具备广泛性的优化呢?有的!每每,咱们能够对dp经过滚动数组省空间、压缩一维省时间、状态压缩二进制、矩阵优化转问题、线段树优化区间、树状数组O(logn) etc.

有没有被一大堆优化整晕?休息一下,喝杯茶,咱们稍后继续讨论动态规划的优化:Dynamic Programming从入土到复活


写了一下午,感受写完本身已经先一步入土惹……

撒花✿✿ヽ(°▽°)ノ✿

原文出处:https://www.cnblogs.com/firstfan/p/10164903.htmlhtml

相关文章
相关标签/搜索