1 模版介绍
拓扑排序+动态规划结合使用可以求有向无环图的最长路或最短路。
-
数据存储:
· din数组:存储各顶点入度数。
· q队列:让入度为0的点入队,进行拓扑排序。
· score数组:存储以顶点v为终点的最值。
· ans变量:存储最长路或最短路的值。
· G数组:邻接表(也可以用链式前向星)。
· W数组:与G数组下标对应,存储权值。 -
算法过程:
step 1:输入各边的起点、终点和权值。更新数组din、G和W。
step 2:如果求最长路,初始化score数组元素为一个较小的值(求最长路反之)。
step 3:通过判断din的值,让入度为0的点进入队列。
step 4:使用拓扑排序求最值:
1)获取队头元素存在ft中,队头元素出队。
2)用循环变量i遍历邻接表G[ft],令din[G[ft][i]]–。更新ans的值为min(ans,score[ft]+W[ft][i])。同时也要对score[G[ft][i]]做更新,score[G[ft][i]]=min(score[G[ft][i]],score[ft]+W[ft][i])。
3)判断如果G[ft][i]的入度为0,则该点入队
一直这样做,直到队列为空。
step 5:输出ans。 -
算法解释:
求最值我们常用动态规划,动态规划要有最优子结构,并且无后效性,为了做到这一点,我们可以用拓扑排序把先遍历到的节点最值确定再扩展到后续节点。
① 我们首先让入度为0的点入队,保证起点合法。
② 在扩展路径的过程中,我们通过遍历队头元素的邻接表,把所有可能的点都试一遍,如果发现当前节点最值加上邻接的点的值大于当前最值ans,则更新ans。同时,更新邻接的点的最值,以便后续用该点扩展的最值也是正确的。
③ 当一个点v的入边全部被遍历后,score[v]便得到了以v为终点的最值。我们这时把点v加入队列,继续扩展。
例题
例题1 洛谷P1807
解题思路(参考洛谷题解)
这道题属于模版题,但是要注意一下题目细节:

输入格式里面说了“每行 3 个整数 u,v,w(u<v),代表存在一条从 u 到 v 边权为 w 的边”,可以知道1号点是没有入边的,因为没有编号小于1。还有一个要注意的点是,可能有除了1号点外入度为0的点,而题目要我们求1,n间的最长路径,所以起点只能是1号点,其他入度为0的点无法抵达,我们要先用拓扑排序把这些点相邻的节点的度数减去。然后节点1入队,再用一次拓扑排序去求最值。
AC代码
#include<bits/stdc++.h>
#define maxn 1600
#define inf 0x3f3f3f3f
using namespace std;
long long ans=-inf,n,m,din[maxn],score[maxn];
queue<int> q;
vector<int> G[maxn],W[maxn];
int main(){
cin>>n>>m;
for(int i=1;i<=m;i++){
int u,v,w;
cin>>u>>v>>w;
G[u].push_back(v);
W[u].push_back(w);
din[v]++;
}
for(int i=2;i<=n;i++) {
score[i]=-inf;
if(din[i]==0)
q.push(i);
}
while(q.size()){
int ft=q.front();
q.pop();
for(int i=0;i<G[ft].size();i++){
if(--din[G[ft][i]]==0) q.push(G[ft][i]);
}
}
q.push(1);
while(q.size()){
int ft=q.front();
q.pop();
for(int i=0;i<G[ft].size();i

最低0.47元/天 解锁文章
3万+

被折叠的 条评论
为什么被折叠?



