题外话:(发现原定的周一更新咕了)
此文是用于传授"动态规划"的本质,公司面试题目可能会略低于本篇文章所讲,会提到各类优化算法到底"应该去优化什么"。阅读时间约10分钟。(如滚动数组、各种数据结构/CDQ分治优化等)
问题开始之前,先谈谈一些定义:
有向图
注意无向图其实是有向图的一种特殊形式,只要建立边
路径:多条边连成的一条长链,
无环:对于任意点对
某个点的入度:进入这个点的边的个数。
简单路径:路径上的顶点都不相同的路径。
有向无环图:有向图+无环条件,简称为
好了,让我们进入正题。
给定一个有向无环图
现在要求
由于最短路满足性质:如果
这里不要想着任何最短路算法。
让我们直接用简单的代码,描述这个"求
用
void dfs(Point u)
{
for all edge(u, v, w) in G:
if dist[v] > dist[u] + w
dist[v] := dist[u] + w
dfs(v)
}
可是这样复杂度肯定太高了,因为存在这种情况:
- 点
目前的值是最优解,点
的"当前最优解"更新了一些点的dist.
- 之后,点
被其它点(在
之前更新的点)的解所更新,导致
需要继续更新"之前它更新过的那些点的dist"。
- 这造成了冗余,那能不能存在一种更新顺序,使得 用
去更新其它点的情况下,就已经是最优解呢?(也就是说,在dfs(v)执行时,保证
既然说了它是一个有向无环图,不如我们用拓扑排序的做法:每次取出入度为0的点
void
显而易见地,按照这种顺序,在每次加入一个点
参考一下这张图片:
![92f734e359229bd63ff0e875b663e2a1.png](https://img-blog.csdnimg.cn/img_convert/92f734e359229bd63ff0e875b663e2a1.png)
考虑一下进入队列的顺序,那么可以是{S,1,2,3,4,T}或者{S,2,1,3,4,T}
任意一种更新最短路的顺序,都能保证当前进入队列的点的dist值为最短路的长度。
比如现在已经做完了{S,1,2},删除掉了从{S,1,2}出发的每条边,图是这样的:
![046cd6c7c71585815769baca527cf6b5.png](https://img-blog.csdnimg.cn/img_convert/046cd6c7c71585815769baca527cf6b5.png)
由于已经没有能进入到点3的边了,也就意味着"能更新点3的最短路的点已经不存在了"。
此时dist[3]的值,就是最终的值,它已经走到了最优解。这和原始dfs代码中能得到的dist[3]的值是完全相同的。
那么继续用3去更新4的最短路。
结论:如果按照一定顺序(拓扑序)对图上每个点进行更新,当这个点去用来更新其它点的最短路时,该点的最短路已经被算出,且为最优解。
也就是说,按照拓扑序进行计算最短路,就可以保证每一步都是最优解,每一步都是正确的。
废话了这么半天,来看题吧。
![39227d36db1b5cd792a0025acff7c8ad.png](https://img-blog.csdnimg.cn/img_convert/39227d36db1b5cd792a0025acff7c8ad.png)
给定一个序列
啥?刚刚不是还在说图嘛?怎么跳到数字上来了?
别急。让我们对着序列傻傻地建立一张有
令
建立以下的边
void Build()
{
dist[0] = 0
i = 0
while(i <= n)
if i < n then addedge(i, i + 1, 0);
addedge(0, i, 0);
}
求这张图中,0到所有点
这个图显然是一个有向无环图,也就可以通过上面的拓扑排序+求最短路做。
什么?你讲了这么半天,到底啥是动态规划啊?
"如果一个题可以用图论思想,建图之后用最短/最长路算法去解决,并且这个图是一个有向无环图,那么这个问题就是动态规划。" (Dynamic Programming,简称dp)
但这类题目真的需要用图论方法做嘛?答案是不需要。
只要你设计出合理的"状态",再找出一个合适的更新的"顺序"(使得轮到这个点更新其它的点时,保证这个点是最优解)以及可以更新状态的递推式,就能够跳出图论方法去解这道题。
这道题的主流解法是这样的,令
1.通过和
2.以自己一个元素结尾。
(其实f[i]就对应之前的dist[i]。)
那么不难写出递推式:
这里, 状态就是代表以
"顺序"就是从1到n,并不是从n到1,也不是其它奇奇怪怪的顺序,这样的顺序递推能保证f[i]是最优解。 递推式呢,就是结尾的序列的最小的和
了。
讲到这里,其实已经很明确了:
思路如下:
首先设计状态,也就是"xx代表xx",随便举一些例子:"f[i]代表前i个数字的最优解" "g[i][j]代表第i天还剩j元钱的最优解" "h[i][j][k]代表投掷了i次筛子,j次是1,k次小于等于5的概率"
之后不需要设计更新状态的顺序
然后设计递推式:
如果设计好了递推式,比如长成这个样子:
显然从n到1的顺序更新就能直接得到答案。
如果设计出来的递推式有一个奇怪的形式:
似乎"没有一个可以入手的点"。脑袋里建出图来看看:这个图形成了一个环!
那这时候一般有两种情况:1. 这个题根本就不是一个动态规划的题 2. 状态设计的不好,导致递推式形成了环。 解决方法很暴力:推翻一开始设计的状态,重新来过。
事实上动态规划的难点就是在于1. 设计出来一个正确的状态 2. 设计一个正确的递推式
(其实练多了就会发现永远有一大票做不出来的动态规划题)
上面这些就是有向无环图与动态规划的联系了,接下来随便做几道题冷静一下:
当然动态规划题目的特征一般就是:
- 最优解问题 2. 计数问题
那些网上说的几类"树形dp,区间dp,背包dp,……"都是不同的dp题目,但其实只要设计好状态就可以做,所以你会的dp题目和你做过的dp题目类型(不是数目)有多少并没有任何联系,只和数目有关。
并不是说你做了一道树形dp的题,之后所有树形dp的题就都会了,恰恰相反,如果你只做了一道树形dp的题,那么下次考树形dp一定是不会的。
好了鸡汤就说这么多,让我们看一些题目:
这里有背包问题的讲解blog.csdn.net题目1:旅行商简化版
有n个二维平面的点,一个人从西边的某个点出发,到达最东边的某个点,然后返回,但是返回的时候不能经过原来经过的点,所有点(除了起点)都需要走恰好一遍。求最短的距离。 (
解法:
设
- 解决不了"检验每个点是否走过"
- 检验不了回去怎么不经过同一个点。
这是完全错误的状态设计,一眼就可以排除掉的垃圾状态。
好了,考虑正确的状态:
因为可以把路程拆分成"向东"和"向西"两部分,设
设计递推式:
设k = max(i,j) + 1,则有:
f[i][k] = min(f[i][k],f[i][j] + dis[j,k]);
f[k][j] = min(f[k][j],f[i][j] + dis[i,k]);
这当然也是递推式……因为这样做能算出最终答案,也能保证答案是最优的。
题目2:vijos 1243 (摘自我自己的博客)
![af77f44d6054d2c30b1b88f066adbc3c.png](https://img-blog.csdnimg.cn/img_convert/af77f44d6054d2c30b1b88f066adbc3c.png)
注意这个题目是可以用单调队列优化的,不要忘记。
题目3: BZOJ 3036
Description
随着新版百度空间的下线,Blog宠物绿豆蛙完成了它的使命,去寻找它新的归宿。
给出一个有向无环的连通图,起点为1终点为N,每条边都有一个长度。绿豆蛙从起点出发,走向终点。
到达每一个顶点时,如果有K条离开该点的道路,绿豆蛙可以选择任意一条道路离开该点,并且走向每条路的概率为 1/K 。
现在绿豆蛙想知道,从起点走到终点的所经过的路径总长度期望是多少?
Input
第一行: 两个整数 N, M,代表图中有N个点、M条边
第二行到第 1+M 行: 每行3个整数 a b c,代表从a到b有一条长度为c的有向边
Output
从起点到终点路径总长度的期望值,四舍五入保留两位小数。
--------
题解很简单,考虑状态
至于顺序的选取,采用记忆化搜索/拓扑排序都可以。
其实还有其他的一些dp题目还在整理……但是就不放上来了,接下来讲讲dp的优化部分。
1.斜率优化,也是似乎大家最不会的一个优化。
其实很简单啦~如果这里听不懂以后还会开专栏的。
记得BJWC2017出现过一个奇妙的题目:
其中
(遇到这类题目,凡是遇到一个j一个i一个平方,往斜率优化的方向猜我就没输过)
展开。
由于
所以我们可以简单地化成这样的形式:
求
这就是斜率优化。
接下来维护之前的点的凸包,以及在凸包上二分即可。
这里可以考虑用CDQ分治。(这些名词是啥以后会讲到……)
2.各种数据结构优化:
一般很容易就能看出来的。 比如这个:
由于是一个二维偏序的形式,而且由于按照动态规划的顺序是能保证
用各种数据结构都能迅速解决偏序问题,最典型的就是平衡树、线段树、树状数组balabala……
总之暂时是告一段落了,题目并不是很多,但本质地说明了有向无环图和动态规划之间的联系,也稍微总结了各种动态规划里面的优化……
但是看懂这篇文章对于学会做题应该没什么帮助,还是要多做题,独立去设计状态及状态的递推式。
ps:微信公众号是同名的,欢迎关注~
我的其它文章:
制糕神的算法工坊:OI/ACM中的哈希表,一些哈希算法以及题目zhuanlan.zhihu.com![573178fd21f4196005cd32711b50fd95.png](https://img-blog.csdnimg.cn/img_convert/573178fd21f4196005cd32711b50fd95.png)
我的近期回答:
八数码为什么可以归结为图的最短路问题?www.zhihu.com![51a32db019db19d09924275a9d0793de.png](https://img-blog.csdnimg.cn/img_convert/51a32db019db19d09924275a9d0793de.png)
其实这里"数字对图上的点的转化"和这篇文章的思路差不太多。