题目大意
原题链接:Acwing 342:道路与航线
各城市之间有道路也有航线,道路是双向的且权值为正,航线是单向的且权值有负,保证航线连接的两个城市ab,只能从a到b,而不能以任何途径从b到a,路的权值是花费。
给定一个起点,求从起点到其余所有城市的最小花费。
思路
根据题意,所有道路相连的城市都是一个连通的无向图,将这样的城市群看作一个团,整张图会有很多个团,每个团之间有单向的边,权值可能是负数,如下图,黑边是道路,红边是航线:
既有正边又有负边的单源最短路问题应该怎么做?dij不能处理负权边,因此肯定首先会想到spfa,但spfa已死 ,很容易被一个菊花图卡掉,这道题就被卡掉了。所以我们就要看这个图的性质。每个团内部可以用堆优化的dijstra来求最短路,而每个团之间一定无环(题意,没有任何途径从航线终点到航线起点),那么将每个团做一个拓扑排序,然后线性的扫描一遍,就可以处理团之间的边了。我们用id数组存每个点所属的团的编号,block的vector数组来存每个团内有哪些点。
然后按照拓扑序对每个团内的点做一遍堆优化的dij,边拓扑排序边做dij:当团内dij时若访问到团外的点,就将点所在的团的入度减一(topsort),然后正常更新dis数组即可。(具体步骤可以看代码详解)
这样子就灵活的处理掉了dij不能处理负权边的问题,因为所有团是按拓扑序排的,所以线性的扫一遍求最短路是没问题的。而每个团做dij时,将当前点走到的点记为v,若v是团内的点就正常更新dis,判断入堆即可;若v是团外的点,则该边可能是负数,我们就直接更新dis[v]即可,然后将v所在团的入度减一,若入度为0,将v所在的团加入拓扑序列。然后我们判断若dis[i] > INF/2,就说明没有路到i点,因为若i的团在起点的团的拓扑序的前面,且i之前有团用负边更新到了i点,因此i点的距离可能比INF小一点,所以只需用一个较大的数来判断,我们取INF/2来判断,若大于他,都说明没有路,输出NO PATH,其余输出dis[i]即可。
代码详解
#include<iostream>
#include<cstring>
#include<queue>
#include<vector>
#define x first
#define y second
using namespace std;
typedef pair<int,int> PII;
const int N = 25010,M = 150010,INF = 0x3f3f3f3f;
int n,r,p,s;
int e[M],ne[M],w[M],h[N],idx; //链式前向星建图
int id[N],dis[N],d[N],bcnt; //id是每个点所属的连通块编号,dis是到各点的距离,d是入度,bcnt是连通块编号
bool vis[N];