【工作室课题】道路与航路

Description

农夫约翰正在针对一个新区域的牛奶配送合同进行研究。他打算分发牛奶到T个城镇(标号为1..T),这些城镇通过R条标号为(1..R)的道路和P条标号为(1..P)的航路相连。

每一条公路i或者航路i表示成连接城镇Ai(1<=A_i<=T)和Bi(1<=Bi<=T)代价为Ci。每一条公路,Ci的范围为0<=Ci<=10,000;由于奇怪的运营策略,每一条航路的Ci可能为负的,也就是-10,000<=Ci<=10,000。

每一条公路都是双向的,正向和反向的花费是一样的,都是非负的。

每一条航路都根据输入的Ai和Bi进行从Ai->Bi的单向通行。实际上,如果现在有一条航路是从Ai到Bi的话,那么意味着肯定没有通行方案从Bi回到Ai。

农夫约翰想把他那优良的牛奶从配送中心送到各个城镇,当然希望代价越小越好,你可以帮助他嘛?配送中心位于城镇S中(1<=S<=T)。

Input

输入的第一行包含四个用空格隔开的整数T,R,P,S。

接下来R行,描述公路信息,每行包含三个整数,分别表示Ai,Bi和Ci。

接下来P行,描述航路信息,每行包含三个整数,分别表示Ai,Bi和Ci。

Output

输出T行,分别表示从城镇S到每个城市的最小花费,如果到不了的话输出NO PATH。

Sample Input 1 

6 3 3 4
1 2 5
3 4 5
5 6 10
3 5 -100
4 6 -100
1 3 -10

Sample Output 1

NO PATH
NO PATH
5
0
-95
-100

Source

蓝桥杯练习系统 ID: 22 原题链接: http://lx.lanqiao.cn/problem.page?gpid=T22

完整代码

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <queue>
#include <stack>
#include <vector>
 
#define clr(a,b) memset(a, b, sizeof(a))
 
using namespace std;
 
const int N = 25050;
const int E = 150500;
 
//邻接表
int h[N], v[E], w[E], nxt[E], el;
void initEdge() {
    clr(h, -1); el = 0;
}
void addEdge(int x, int y, int z) {
    v[el] = y; w[el] = z; nxt[el] = h[x]; h[x] = el++;
}
 
//并查集
int rt[N], ra[N];
int Find(int x) {
    if(x != rt[x]) rt[x] = Find(rt[x]);
    return rt[x];
}
void Union(int r1, int r2) {
    int x = Find(r1), y = Find(r2);
    if(x == y) return;
    if(ra[x] > ra[y]) rt[y] = x;
    else {
        rt[x] = y;
        if(ra[x] == ra[y]) ++ra[y];
    }
}
void Init(int n) {
    for(int i=0; i<=n; i++)
        ra[i] = 0, rt[i] = i;
}
 
struct EDGE {
    int u, v, w;
    bool flag;
    EDGE(){}
    EDGE(int x, int y, int z, bool f):u(x), v(y), w(z), flag(f){}
}   edge[E];
 
int edgel;
 
bool visitable[N];
 
void dfs(int x) {
    visitable[x] = true;
    for(int i=h[x]; ~i; i=nxt[i]) {
        if(!visitable[v[i]]) {
            dfs(v[i]);
        }
    }
}
 
int indegree[N];
bool vis[N];
 
//链表
int lh[N], lel, lv[E], lnxt[E];
void initLink() {
    clr(lh, -1); lel = 0;
}
void addLink(int x, int y) {
    lv[lel] = y; lnxt[lel] = lh[x]; lh[x] = lel++;
}
 
int dis[N];
bool tag[N];
 
int main() {
    int n, r, p, s;
    scanf("%d%d%d%d", &n, &r, &p, &s);
    Init(n);
    initEdge();
    edgel = 0;
    int x, y, z;
    for(int i=0; i<r; i++) {
        scanf("%d%d%d", &x, &y, &z);
        addEdge(x, y, z);
        addEdge(y, x, z);
        edge[edgel++] = EDGE(x, y, z, false);
        Union(x, y);
    }
    for(int i=0; i<p; i++) {
        scanf("%d%d%d", &x, &y, &z);
        addEdge(x, y, z);
        edge[edgel++] = EDGE(x, y, z, true);
    }
    dfs(s);
    initEdge();
    initLink();
    for(int i=0; i<edgel; i++) {
        if(visitable[edge[i].u] && visitable[edge[i].v]) {
            addEdge(edge[i].u, edge[i].v, edge[i].w);
            if(edge[i].flag) {
                ++ indegree[Find(edge[i].v)];
                addLink(Find(edge[i].v), edge[i].v);
            } else {
                addEdge(edge[i].v, edge[i].u, edge[i].w);
            }
        }
    }
    stack<int> zeroDegree;
    priority_queue<pair<int,int> > que;
    clr(dis, 0x3f);
    dis[s] = 0;
    que.push(make_pair(0, s));
    while(!que.empty() || !zeroDegree.empty()) {
        if(que.empty()) {
            int x = zeroDegree.top(); zeroDegree.pop();
            for(int i=lh[x]; ~i; i=lnxt[i]) {
                int y = lv[i];
                if(!vis[y]) {
                    vis[y] = true;
                    que.push(make_pair(-dis[y], y));
                }
            }
        } else {
            int x = que.top().second; que.pop();
            if(tag[x]) continue;
            tag[x]  = true;
            for(int i=h[x]; ~i; i=nxt[i]) {
                int y = v[i];
                if(!tag[y] && dis[y] > dis[x] + w[i]) {
                    dis[y] = dis[x] + w[i];
                    if(Find(x) == Find(y)) {
                        que.push(make_pair(-dis[y], y));
                    }
                }
                if(Find(x) != Find(y)) {
                    -- indegree[Find(y)];
                    if(indegree[Find(y)] == 0) {
                        zeroDegree.push(Find(y));
                    }
                }
            }
        }
    }
    for(int i=1; i<=n; i++) {
        if(visitable[i]) {
            printf("%d\n", dis[i]);
        } else {
            puts("NO PATH");
        }
    }
 
    return 0;
}
       

总结

有一些有向边,还有一些无向边,已知不存在回路,求起点到其他各点的最短距离。先输入所有双向道路,然后等方式求出所有连通块,计算出两个数组:每个点属于哪个连通块,每个连通块有哪些点。输入所有航线,同时统计出每个连通块的入度。按照拓扑序一次处理每个连通块。先将所有入度为0的连通块的编号加入队列中。每次从队头取出一个连通块的编号bid将该块中的所有点加入堆中,然后对堆中所有点跑Dijkstra算法。每次取出堆中距离最小的点ver然后遍历ver的所有邻点j,如果id[ver] == id[j],那么如果j能被更新,则将j插入堆中;如果id[ver] != id[j],则将id[j]这个连通块的入度减1,如果减成0了,则将其插入拓扑排序的队列中。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值