
本文共 3817 字,大约阅读时间需要 12 分钟。
DIJ + Topsort + DFS - Roads and Planes G(道路与航线) - 洛谷 P3008
农夫约翰正在一个新的销售区域对他的牛奶销售方案进行调查。
他想把牛奶送到T个城镇,编号为1~T。
这些城镇之间通过R条道路 (编号为1到R) 和P条航线 (编号为1到P) 连接。
每条道路 i 或者航线 i 连接城镇Ai到Bi,花费为Ci。
对于道路,0≤Ci≤10,000;然而航线的花费很神奇,花费Ci可能是负数(−10,000≤Ci≤10,000)。
道路是双向的,可以从Ai到Bi,也可以从Bi到Ai,花费都是Ci。
然而航线与之不同,只可以从Ai到Bi。
事实上,由于最近恐怖主义太嚣张,为了社会和谐,出台了一些政策:保证如果有一条航线可以从Ai到Bi,那么保证不可能通过一些道路和航线从Bi回到Ai。
由于约翰的奶牛世界公认十分给力,他需要运送奶牛到每一个城镇。
他想找到从发送中心城镇S把奶牛送到每个城镇的最便宜的方案。
输入格式
第一行包含四个整数T,R,P,S。
接下来R行,每行包含三个整数(表示一个道路)Ai,Bi,Ci。
接下来P行,每行包含三个整数(表示一条航线)Ai,Bi,Ci。
输出格式
第1…T行:第i行输出从S到达城镇i的最小花费,如果不存在,则输出“NO PATH”。
数据范围
1 ≤ T ≤ 25000 , 1 ≤ R , P ≤ 50000 , 1 ≤ A i , B i , S ≤ T 1≤T≤25000, 1≤R,P≤50000, 1≤A_i,B_i,S≤T 1≤T≤25000,1≤R,P≤50000,1≤Ai,Bi,S≤T
输入样例:
6 3 3 41 2 53 4 55 6 103 5 -1004 6 -1001 3 -10
输出样例:
NO PATHNO PATH50-95-100
分析:
题 意 : 题意: 题意:
给 定 一 个 由 T 个 点 , R 条 非 负 权 无 向 边 , P 条 有 向 边 ( 正 、 负 权 ) 构 成 的 图 , 给定一个由T个点,R条非负权无向边,P条有向边(正、负权)构成的图, 给定一个由T个点,R条非负权无向边,P条有向边(正、负权)构成的图,
给 定 起 点 S , 求 S 到 所 有 点 的 最 短 距 离 , 若 无 通 路 , 输 出 " N O P A T H " 。 给定起点S,求S到所有点的最短距离,若无通路,输出"NO\ PATH"。 给定起点S,求S到所有点的最短距离,若无通路,输出"NO PATH"。
由 题 意 , 所 有 有 向 边 不 构 成 环 , 即 有 向 边 之 间 是 一 个 拓 扑 图 关 系 。 由题意,所有有向边不构成环,即有向边之间是一个拓扑图关系。 由题意,所有有向边不构成环,即有向边之间是一个拓扑图关系。
我 们 可 以 将 无 向 边 连 接 的 两 个 点 归 入 到 一 个 连 通 块 中 , 各 个 连 通 块 之 间 由 有 向 边 连 接 。 我们可以将无向边连接的两个点归入到一个连通块中,各个连通块之间由有向边连接。 我们可以将无向边连接的两个点归入到一个连通块中,各个连通块之间由有向边连接。
这 样 , 连 通 块 内 部 可 以 通 过 d i j k s t r a 来 跑 最 短 路 , 连 通 块 之 间 由 于 存 在 负 权 边 , 可 通 过 拓 扑 序 来 求 最 短 路 。 这样,连通块内部可以通过dijkstra来跑最短路,连通块之间由于存在负权边,可通过拓扑序来求最短路。 这样,连通块内部可以通过dijkstra来跑最短路,连通块之间由于存在负权边,可通过拓扑序来求最短路。
在 连 通 块 中 跑 d i j k s t r a 时 : 在连通块中跑dijkstra时: 在连通块中跑dijkstra时:
每 次 我 们 先 将 同 一 连 通 块 中 的 所 有 点 加 入 堆 中 , 然 后 开 始 扩 展 , 每次我们先将同一连通块中的所有点加入堆中,然后开始扩展, 每次我们先将同一连通块中的所有点加入堆中,然后开始扩展,
若 扩 展 到 的 点 是 同 一 连 通 块 中 的 , 就 将 该 点 加 入 堆 中 继 续 扩 展 。 若扩展到的点是同一连通块中的,就将该点加入堆中继续扩展。 若扩展到的点是同一连通块中的,就将该点加入堆中继续扩展。
若 扩 展 到 的 点 不 是 同 一 连 通 块 中 的 , 就 将 该 点 所 在 的 连 通 块 的 入 度 减 1 , 当 某 个 连 通 块 的 入 度 等 于 0 时 , 再 将 该 连 通 块 加 入 到 拓 扑 排 序 的 队 列 中 去 。 若扩展到的点不是同一连通块中的,就将该点所在的连通块的入度减1,当某个连通块的入度等于0时,\\再将该连通块加入到拓扑排序的队列中去。 若扩展到的点不是同一连通块中的,就将该点所在的连通块的入度减1,当某个连通块的入度等于0时,再将该连通块加入到拓扑排序的队列中去。
注意:
由 于 存 在 负 权 回 路 , 故 不 连 通 的 两 个 点 间 的 距 离 可 能 会 不 等 于 i n f , 只 要 大 于 某 个 较 大 的 数 值 , 我 们 就 认 为 不 连 通 。 由于存在负权回路,故不连通的两个点间的距离可能会不等于inf,只要大于某个较大的数值,我们就认为不连通。 由于存在负权回路,故不连通的两个点间的距离可能会不等于inf,只要大于某个较大的数值,我们就认为不连通。
代码:
#include#include #include #include #include #include #define P pair using namespace std;const int N=25010, M=150010, inf=0x3f3f3f3f;int n,mr,mp,S;int e[M],w[M],ne[M],h[N],idx;int id[N],bcnt,din[N];vector block[N];int dis[N];bool st[N];queue q;void add(int a,int b,int c){ e[idx]=b,w[idx]=c,ne[idx]=h[a],h[a]=idx++;}void dfs(int u,int bid){ id[u]=bid; block[bid].push_back(u); for(int i=h[u];~i;i=ne[i]) { int j=e[i]; if(!id[j]) dfs(j,bid); //若与u相邻的点不在连通块中,再加入 }}void dijkstra(int u){ memset(st,false,sizeof st); priority_queue ,greater
> heap; for(auto v : block[u]) heap.push({ dis[v],v}); while(heap.size()) { P t=heap.top(); heap.pop(); int ver=t.second; if(st[ver]) continue; st[ver]=true; for(int i=h[ver];~i;i=ne[i]) { int j=e[i]; if(dis[j]>dis[ver]+w[i]) { dis[j]=dis[ver]+w[i]; if(id[ver] == id[j]) heap.push({ dis[j],j}); } if(id[ver]!=id[j] && (--din[id[j]]) == 0) q.push(id[j]); } }}void topsort(){ memset(dis,0x3f,sizeof dis); dis[S]=0; for(int i=1;i<=bcnt;i++) if(!din[i]) q.push(i); while(q.size()) { int t=q.front(); q.pop(); dijkstra(t); }}int main(){ scanf("%d%d%d%d",&n,&mr,&mp,&S); memset(h,-1,sizeof h); int a,b,c; while(mr--) { scanf("%d%d%d",&a,&b,&c); add(a,b,c),add(b,a,c); } for(int i=1;i<=n;i++) if(!id[i]) dfs(i,++bcnt); while(mp--) { scanf("%d%d%d",&a,&b,&c); add(a,b,c); din[id[b]]++; } topsort(); for(int i=1;i<=n;i++) if(dis[i]>inf/2) puts("NO PATH"); else printf("%d\n",dis[i]); return 0;}
发表评论
最新留言
关于作者
