前言
数据结构,一门数据处理的艺术,精巧的结构在一个又一个算法下发挥着他们无与伦比的高效和精密之美,在为信息技术打下坚实地基的同时,也令无数开发者和探索者为之着迷。
也因如此,它作为博主大二上学期最重要的必修课出现了。由于大家对于上学期C++系列博文的支持,我打算将这门课的笔记也写作系列博文,既用于整理、消化,也用于同各位交流、展示数据结构的美。
此系列文章,将会分成两条主线,一条“数据结构基础”,一条“数据结构拓展”。“数据结构基础”主要以记录课上内容为主,“拓展”则是以课上内容为基础的更加高深的数据结构或相关应用知识。
欢迎关注博主,一起交流、学习、进步,往期的文章将会放在文末。
随着学习的深入,相信各位读者的实力已经可以对付计算出任意两点间最短路径的问题了。那么更进一步的,在这一节我们要讨论关于最短路径的计数问题。在计算出任意两点间的最短路径长度的基础上,我们还需要统计出这些最短路径的方案数。
这个问题,乍一看感觉有些不同寻常,因为不管怎么说,方案数相较于长度来说总还是不好把握的。我们在思考统计方案数的算法的时候总是容易囿于对起点到终点路径规划决策的模拟。
朴素的解决思路多是先在纸上推演从一起点到终点的路径,一条一条的画出最短路径。小规模的时候,找出答案不是难事,但是一旦数据量较大,笔算就会漏洞百出,更何况一旦准备将其付诸于程序,就像是秀才遇到兵,发现根本无从下手。。。。
所以解决问题的关键就是要跳出朴素的思维方式,用一种新的视角来审视这个问题。不妨先从回顾计算最短路径长度的算法开始。
floyd算法求最短路径
任意两点间的最短路径问题,解决的方案是选择不停地枚举中间点来对路径进行“松弛”,让各点之间不断挑选更优的路径从而将距离边的更短。
如下图,对于点对14来说,可以经由中间点2,走1->2->4这条更短的路径。
当然,能用来更新最短路径的中间点也可以不是直接相连的顶点,例如下图:
点对15的最短路径可能由3更新成为5。不过在此之前,13和35的距离应该分别被2和4更新成为2.
可以看到,在这个问题中,有个关键的状态定义,点对 ( i , j ) (i,j) (i,j)之间的最短路径长度 d i j d_{ij} dij。
这个定义让我们只关心点与点之间的路径长度关系而非具体路径。
进一步的,根据这个状态定义,可以找到状态间转移方案:经过中间点k的点对ij的最短路径就是:
d i j = m i n ( d i k + d k j , d i j ) d_{ij}=min(d_{ik}+d_{kj},d_{ij}) dij=min(dik+dkj,dij)
那么在算法的初始,将最短路径长度初始化:
d i i = 0 d_{ii}=0 dii=0
d i j = i n f ( i j 之 间 没 有 边 相 连 ) d_{ij}=inf\ (ij之间没有边相连) dij=inf (ij之间没有边相连)
d i j = l e n i j ( i j 之 间 有 长 度 为 l e n i j 的 边 链 接 ) d_{ij}=len_{ij}\ (ij之间有长度为len_{ij}的边链接) dij=lenij (ij之间有长度为lenij的边链接)
于是这个最短路径长度的算法问题就可以解了,步骤如下:
- 依次枚举所有中间节点k,更新其余点对
- 枚举非中间节点的点对ij
- 使用中间节点k对ij之间的最短路径长度进行松弛
代码实现如下:
int dis[N][N];//最短路径数组,ij最短路径为dis[i][j]
void floyd(int n){
for(int k = 1;k <= n;k++){
//枚举中间节点
for(int i = 1;i <= n;i++){
//枚举非中间节点的点对
if(i == k)continue;
for(int j = i + 1;j <= n;j++){
if(j == k)continue;
if(dis[i][j] > dis[i][k] + dis[k][j]){
//松弛最短路径
dis[i][j] = dis[i][k] + dis[k][j];
dis[j][i] = dis[i][k] + dis[k][j];
}
}
}
}
}
两点间最短路径方案数
要统计最短路径的方案数,没有最短路径长度肯定是不行的,所以以下的算法要建立在floyd算法之上。也就是说,方案数问题要在floyd的过程中顺手解决
前面说过,要解决最短路径的方案数统计问题。我们要跳出朴素的思维方案,借鉴最短路径长度的建模分析方法。
不知各位是否注意到了上文在分析最短路径算法时标记出了三个模块,他们分别是:
状 态 、 转 移 、 初 始 化 状态、转移、初始化 状态、转移、初始化这是解决路径长度问题的关键所在,也就是常常提到floyd用到的动态规划思想。
现在我们可以从动态规划的角度借助这三个模块依葫芦画瓢来分析最短路径方案数的问题。(当然这里对状态和转移的描述未必在学术上严谨,主要是便于分析解决问题。)
定状态
依照最短路径的状态 d i j d_{ij} d