POJ3114 Countries in War 【强连通分量】

题目链接:http://poj.org/problem?id=3114

题意:
有几个城市连通,如果几个城市互相连通,则他们传送信件不需要时间,否则就需要一些时间传送,现在问你某两个城市之间传送信件最少需要多长时间

题解:
最短路+强连通分量,两个模板直接搞定。
代码:

// memset 只能赋值 -1 和 0,其余用 fill() 
#include <queue>
#include <cstdio>
#include <cstring>
#include <algorithm>

using namespace std;

const int size = 505, size2 = 505*505, inf = 1 << 26;

struct edges {
    int to, next;
} ed[size2];

int n, m;
int dfn[size], low[size], head[size], stack[size], instack[size], sccno[size], w[size][size];
int mp[size][size];
int dis[size], vis[size];// 最短路数组
int cnt = 0, qcnt = 0, top = 0, scccnt = 0;

void add(int u, int v) {
    ed[cnt].to = v;
    ed[cnt].next = head[u];
    head[u] = cnt ++;
}

void init() {
    cnt = qcnt = scccnt = top = 0;
    memset(ed, 0, sizeof(ed));
    memset(dfn, 0, sizeof(dfn));
    memset(low, 0, sizeof(low));
    memset(head, -1, sizeof(head));
    memset(stack, 0, sizeof(stack));
    memset(instack, 0, sizeof(instack));
    memset(instack, 0, sizeof(instack));
    memset(sccno, 0, sizeof(sccno));
    memset(vis, 0, sizeof(vis));
    for ( int i = 0; i <= n; i ++ ) for ( int j = 0; j <= n; j ++ ) w[i][j] = inf;
    for ( int i = 0; i <= n; i ++ ) for ( int j = 0; j <= n; j ++ ) mp[i][j] = inf;
}

void Tarjan(int x) {
    dfn[x] = low[x] = ++qcnt;
    instack[x] = 1;
    stack[++top] = x;

    for ( int i = head[x]; i != -1; i = ed[i].next ) {
        int y = ed[i].to;
        if(!dfn[y]) {       // 检查是否被访问过
            Tarjan(y);
            low[x] = min(low[x], low[y]);
        } else if(instack[y]) {
            low[x] = min(low[x], low[y]);
                            //== dfn[i]
        }
    }

    if(dfn[x] == low[x]) {
        int temp;
        scccnt ++;
        do {
            temp = stack[top --];
            instack[temp] = 0;
            sccno[temp] = scccnt;
        } while( temp != x );
    }
}

void spfa(int s) {
    for ( int i = 0; i <= n; i ++) dis[i] = inf;
    dis[s] = 0;
    queue<int> q;
    q.push(s);
    vis[s] = 1; 
    while(!q.empty()) { 
        int t = q.front(); q.pop();
        vis[t] = 0;
        for ( int i = 1; i <= scccnt; i ++ ) {
            if(dis[i] > dis[t]+mp[t][i]) {
                dis[i] = dis[t]+mp[t][i];
                if(!vis[i]) {
                    q.push(i);
                    vis[i] = 1;
                }
            }
        }
    }
}

int main() {
    // freopen("3114.in", "r", stdin);
    while( scanf("%d %d", &n, &m), n ) {
        init();
        int a, b, c;
        for ( int i = 1; i <= m; i ++ ) {
            scanf("%d %d %d", &a, &b, &c);
            add(a, b);
            w[a][b] = min(w[a][b], c);
        }

        for ( int i = 1; i <= n; i ++ ) {
            if(!dfn[i]) Tarjan(i);
        }

        for ( int i = 0; i <= n; i ++ ) mp[i][i] = 0;
        for ( int i = 1; i <= n; i ++ ) {
            for ( int j = 1; j <= n; j ++ ) {
                if(i != j && sccno[i] != sccno[j] && w[i][j] != inf) {
                    mp[sccno[i]][sccno[j]] = min(mp[sccno[i]][sccno[j]], w[i][j]);
                }
            }
        }

        int k;
        scanf("%d", &k);
        for ( int i = 0; i < k; i ++ ) {
            int a, b;
            scanf("%d %d", &a, &b);
            if(sccno[a] == sccno[b]) {      // 属于一个强连通分量
                puts("0");
            } else {
                spfa(sccno[a]);
                if(dis[sccno[b]] < inf) printf("%d\n", dis[sccno[b]]);
                else puts("Nao e possivel entregar a carta");
            }
        }
        puts("");
    }

    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值