【拓扑排序+动态规划】例题及解题报告

1 模版介绍

拓扑排序+动态规划结合使用可以求有向无环图的最长路或最短路。

  1. 数据存储:
    · din数组:存储各顶点入度数。
    · q队列:让入度为0的点入队,进行拓扑排序。
    · score数组:存储以顶点v为终点的最值。
    · ans变量:存储最长路或最短路的值。
    · G数组:邻接表(也可以用链式前向星)。
    · W数组:与G数组下标对应,存储权值。

  2. 算法过程:
    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。

  3. 算法解释:
    求最值我们常用动态规划,动态规划要有最优子结构,并且无后效性,为了做到这一点,我们可以用拓扑排序把先遍历到的节点最值确定再扩展到后续节点。
    ① 我们首先让入度为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
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值