蓝桥杯练习题-《道路和航路》解题报告

来源:互联网 发布:js时间滑动选择插件 编辑:程序博客网 时间:2024/05/22 14:13

蓝桥杯练习题-《道路和航路》解题报告

题目链接:http://lx.lanqiao.org/problem.page?gpid=T22

题目描述如下:

农夫约翰正在针对一个新区域的牛奶配送合同进行研究。他打算分发牛奶到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)。
输入格式
输入的第一行包含四个用空格隔开的整数T,R,P,S。
接下来R行,描述公路信息,每行包含三个整数,分别表示Ai,Bi和Ci。
接下来P行,描述航路信息,每行包含三个整数,分别表示Ai,Bi和Ci。
输出格式
输出T行,分别表示从城镇S到每个城市的最小花费,如果到不了的话输出NO PATH。
样例输入
6 3 3 4
1 2 5
3 4 5
5 6 10
3 5 -100
4 6 -100
1 3 -10
样例输出
NO PATH
NO PATH
5
0
-95
-100
数据规模与约定
对于20%的数据,T<=100,R<=500,P<=500;
对于30%的数据,R<=1000,R<=10000,P<=3000;
对于100%的数据,1<=T<=25000,1<=R<=50000,1<=P<=50000。


这个题目是一个含有负边的最短路问题,可以用SPFA过掉90%的数据,有2组数据过不了,大概是出题人针对SPFA而出的数据。
大概是我实力不济,经过各种尝试没能卡过。于是决定改用dijkstra.
一般来说dijkstra是没法处理含有负权值的图的,但是这题给的图比较特殊。
所有的负权值只可能在航路,也就是单向边中,而所有的单向边都不在环中。
那么整个图就可以看做一个个用公路,也就是双向边连成的块,块与块之间由一些单向边连起来。
强连通缩点之后则变成一个有向无环图……
有向无环图的最短路可以DP出来……


于是想法就有了,首先用并查集维护所有公路连成的块,然后从起点的块开始,在块的内部用dijkstra来求最短路。
块与块之间用类似拓扑排序的方式,维护一个块的入度。当所有指向那个块的单向边的另一端的最短路都已经确定了,才开始在这个块中求最短路。

代码如下:

#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 0
原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 生完孩子肚皮松怎么办 生过孩子肚子松怎么办 生完孩子肚皮痒怎么办 生完小孩肚子松弛怎么办 生了孩子肚子大怎么办 嫁到别的省户口怎么办 孕中期假性宫缩怎么办 频繁的假性宫缩怎么办 显卡风扇不转了怎么办 老是想不好的事怎么办 做人工受孕多囊怎么办 子宫内有囊肿该怎么办 子宫长了个囊肿怎么办 怀孕后不想要该怎么办 20多岁雌激素低怎么办 生完小孩后子宫下垂怎么办 取环之前同房了怎么办 做人流后又怀孕怎么办 仓鼠长了个肿瘤怎么办 过敏留下的黑印怎么办 脸上痘痘发炎了怎么办 脸上皮肤红痒怎么办啊 皮肤有一片红痒怎么办 脸过敏期间很干怎么办 怀孕了用了消糜栓怎么办 乳酸杆菌少或无怎么办 怀孕了白带有异味怎么办 怀孕清洁度iv度怎么办 怀孕了下面有异味怎么办 孕妇尿白细胞1是怎么办 药流期间喂奶了怎么办 20多岁卵巢早衰怎么办 3岁儿童肚子胀气怎么办 肚子又胀又痛怎么办 1岁宝宝肚子胀气怎么办 2岁宝宝肚子胀气怎么办 产后腰粗肚子大怎么办 发烧后腹泻拉水怎么办 又吐又拉还发烧怎么办 喝中药恶心想吐怎么办 生完孩子肛门疼怎么办