DAG的最短路

本文通过一个具体的竞赛题目,介绍了如何在有向无环图(DAG)上使用SPFA算法解决最短路径问题。特别关注了当存在负权边时,Dijkstra算法的不适用性,以及如何利用DAG的拓扑排序特性来优化SPFA算法,使其在规定时间内找到最优解。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目:https://ac.nowcoder.com/acm/contest/329/B

分析:由于带负权,肯定不能用Dijkstra,用spfa可高效解决,但是特殊 DAG 的性质使得 SPFA 算法无法在规定的时间限内求解出答案,此时由DAG的拓扑序列可以很好解决该问题。

因此,放弃vis数组,以拓扑序列来入队

Ac code:

#include<bits/stdc++.h>
using namespace std;
const int maxn=1e5+10;
typedef long long ll;
const ll INF=1e10;
vector<pair<int,ll> >edge1[maxn];
vector<pair<int,ll> >edge2[maxn];
ll dis1[maxn],dis2[maxn];
int dg1[maxn],dg2[maxn];
int n;
ll Spfa(int s,ll dis[],int dg[],vector<pair<int,ll> >edge[])
{
    queue<int>q;
    while(!q.empty()) q.pop();
    for(int i=1;i<=n;i++) dis[i]=INF;
    dis[s]=0;
    for(int i=1;i<=n;i++) if(dg[i]==0) q.push(i);
    while(!q.empty()){
        int u=q.front();
        q.pop();
        for(int i=0;i<edge[u].size();i++){
            int v=edge[u][i].first;
            ll w=edge[u][i].second;
            dis[v]=min(dis[v],dis[u]+w);
            if(--dg[v]==0)
                q.push(v);
        }
    }
    return max(0ll,dis[n]);
}
int main()
{
    int t,m;
    scanf("%d",&t);
    while(t--){
        scanf("%d%d",&n,&m);
        for(int i=0;i<=n;i++) edge1[i].clear(),edge2[i].clear();
        int u,v;
        ll w0,w1,w2;
        for(int i=1;i<=m;i++){
           scanf("%d%d%lld%lld%lld",&u,&v,&w0,&w1,&w2);
           edge1[u].push_back(make_pair(v,-w1+w0)),edge2[u].push_back(make_pair(v,-w2+w0));
           dg1[v]++,dg2[v]++;
        }
        ll ans1=Spfa(1,dis1,dg1,edge1);
        ll ans2=Spfa(1,dis2,dg2,edge2);
        if(ans1<ans2){
            puts("cnznb!!!");
            printf("%lld\n",ans2-ans1);
        }
        else if(ans1>ans2){
            puts("rip!!!");
            printf("%lld\n",ans1-ans2);
        }
        else puts("oof!!!");
    }
    return 0;
}

 

### 拓扑排序实现短路径计算 拓扑排序是一种适用于有向无环图(DAG)的算法,可以用来解决短路径问题。在DAG中,利用拓扑排序可以有效地计算从某个源节点到其他所有节点的短路径[^1]。以下是具体实现方法: #### 1. 拓扑排序的基本原理 拓扑排序的核心思想是通过消除入度为0的节点来逐步减少图中的节点数量。对于每个被移除的节点,更新其相邻节点的距离值。这种方法确保了在计算短路径时,不会出现循环依赖的问题。 #### 2. 算法步骤 - 初始化一个距离数组`dis[]`,将所有节点的距离设为无穷大(`INF`),并将源节点的距离设为0。 - 使用队列或栈进行拓扑排序,首先将所有入度为0的节点加入队列。 - 遍历队列中的节点,对于当前节点的所有邻居节点,更新它们的距离值:`dis[v] = min(dis[v], dis[u] + w)`,其中`u`是当前节点,`v`是邻居节点,`w`是边的权重。 - 如果某个节点的入度变为0,则将其加入队列。 - 终,`dis[]`数组中存储的就是从源节点到其他所有节点的短路径长度。 #### 3. 实现代码 以下是一个基于拓扑排序的短路径计算的Python实现: ```python from collections import deque, defaultdict def shortest_path_topological_sort(graph, in_degree, n, source): # 初始化距离数组和队列 dis = [float('inf')] * (n + 1) dis[source] = 0 queue = deque() # 将入度为0的节点加入队列 for i in range(1, n + 1): if in_degree[i] == 0: queue.append(i) while queue: u = queue.popleft() for v, w in graph[u]: # 更新邻居节点的距离 if dis[v] > dis[u] + w: dis[v] = dis[u] + w # 如果邻居节点的入度降为0,加入队列 in_degree[v] -= 1 if in_degree[v] == 0: queue.append(v) return dis[1:] # 示例用法 if __name__ == "__main__": n = 6 # 节点数 edges = [(1, 2, 5), (1, 3, 3), (2, 4, 1), (3, 2, 2), (3, 5, 4), (4, 6, 2), (5, 6, 1)] # 边 (u, v, w) graph = defaultdict(list) in_degree = [0] * (n + 1) for u, v, w in edges: graph[u].append((v, w)) in_degree[v] += 1 source = 1 result = shortest_path_topological_sort(graph, in_degree, n, source) print("短路径:", result) ``` #### 4. 复杂度分析 - 时间复杂度:O(V + E),其中V是节点数,E是边数。这是因为每个节点和每条边多只会被处理一次。 - 空间复杂度:O(V + E),用于存储图结构和辅助数据结构。 #### 5. 注意事项 如果图中存在环,则无法使用拓扑排序来求解短路径问题,因为拓扑排序的前提是有向无环图。此外,上述算法适用于边权为正或负的情况,但不适用于存在负权环的情况[^1]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值