poj 3114 强连通分量+最短路(间谍通信)

题意:给定一个有向带权图,随后给出q个查询(a,b),求a->b的最短花费。其中如果这两个city属于同一个国家,则花费时间为0。两个city同属于一个国家当且仅当这两个city互相可达。

思路:属于同一个强连通分量的点就是属于同一个国家了。然后进行缩点,在缩点之后的图上求最短路即可。一开始看到多组查询而且点数不多就写了Floyd,但是TLE。随后改成每次查询就来一次SPFA,这样AC。

缩点之后的图也可以用邻接表来存,但是注意边只存放不同连通分支之间最短的那条,所以加边的时候要进行判断,见版本2。

#include <stdio.h>
#include <string.h>
#define N 505
#define INF 0x3fffffff
#define clr(s,t) memset(s,t,sizeof(s))
#define min(a,b) ((a)<(b)?(a):(b))
struct edge{
    int y,next,w;
}e[N*N];
int first[N],dfn[N],low[N],stack[N],inst[N],strong[N],g[N][N];
int q[N],used[N],dis[N];
int n,m,ques,top,id,st,con;
void init(){
    int i,j;
    top = id = st = con = 0;
    clr(dfn, -1);
    clr(first, -1);
    clr(inst, 0);
    for(i = 0;i<N;i++)
        for(j = 0;j<N;j++)
            g[i][j] = INF;
}
void add(int x,int y,int w){
    e[top].y = y;
    e[top].w = w;
    e[top].next = first[x];
    first[x] = top++;
}
void tarjan(int x){
    int i,y;
    dfn[x] = low[x] = ++id;
    stack[st++] = x;
    inst[x] = 1;
    for(i = first[x];i!=-1;i=e[i].next){
        y = e[i].y;
        if(dfn[y] == -1){
            tarjan(y);
            low[x] = min(low[x], low[y]);
        }else if(inst[y])
            low[x] = min(low[x], dfn[y]);
    }
    if(dfn[x] == low[x]){
        con++;
        do{
            strong[stack[--st]] = con;
            inst[stack[st]] = 0;
        }while(stack[st] != x);
    }
}
int relax(int a,int b,int x) {
    if(dis[b] > dis[a]+x){
        dis[b] = dis[a]+x;
        return 1;
    }
    return 0;
}
int spfa(int s,int t){
    int i,j,front,rear,now;
    clr(used, 0);
    front = rear = -1;
    q[++rear] = s;
    used[s] = 1;
    for(i = 1;i<=con;i++)
        dis[i] = INF;
    dis[s] = 0;
    while(front < rear){
        now = q[++front];
        used[now] = 0;
        for(i = 1;i<=con;i++){
            if(relax(now,i,g[now][i]) && !used[i]){
                q[++rear] = i;
                used[i] = 1;
            }
        }
    }
    return dis[t]<INF;
}
int main(){
    while(scanf("%d %d",&n,&m) && (n+m)){
        int i,j,k,a,b,w;
        init();
        for(i = 0;i<m;i++){
            scanf("%d %d %d",&a,&b,&w);
            add(a,b,w);
        }
        for(i = 1;i<=n;i++)
            if(dfn[i] == -1)
                tarjan(i);
        for(i = 1;i<=n;i++)
            for(j = first[i];j!=-1;j=e[j].next){
                a = strong[i];
                b = strong[e[j].y];
                if(a==b)
                    g[a][b] = 0;
                else
                    g[a][b] = min(g[a][b], e[j].w);
            }
        
        scanf("%d",&ques);
        while(ques--){
            scanf("%d %d",&a,&b);
            if(!spfa(strong[a],strong[b]))
                printf("Nao e possivel entregar a carta\n");
            else
                printf("%d\n",dis[strong[b]]);
        }
        putchar('\n');
    }
    return 0;
}

版本2:临界表存缩点新图:

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <map>
#include <queue>
#include <cstdlib>
using namespace std;
#define INF 0x3fffffff
#define clc(s,t) memset(s,t,sizeof(s))
#define N 505
struct edge{
    int y,next,w;
}e[250005],h[250000];
int first[N],f2[N],t2;
int strong[N],instack[N],stack[N],dfn[N],low[N],dis[N];
int n,m,top,id,con,ts;
void add(int x,int y,int w){
    e[top].y = y;
    e[top].w = w;
    e[top].next = first[x];
    first[x] = top++;
}
void add2(int x,int y,int w){//插入新图的边,进行权值判断
    int i;
    for(i = f2[x];i!=-1;i=h[i].next)
        if(h[i].y == y){
            if(h[i].w > w){
                h[i].w = w;
                return;
            }
            break;
        }
    h[t2].y = y;
    h[t2].w = w;
    h[t2].next = f2[x];
    f2[x] = t2++;
}
void init(){
    clc(first,-1);
    clc(f2,-1);
    clc(instack,0);
    clc(dfn,-1);
    top = con = t2 = id = ts = 0;
}
void tarjan(int x){
    dfn[x] = low[x] = ++id;
    instack[x] = 1;
    stack[ts++] = x;
    for(int i = first[x];i!=-1;i=e[i].next){
        if(dfn[e[i].y] == -1){
            tarjan(e[i].y);
            low[x] = min(low[x],low[e[i].y]);
        }else if(instack[e[i].y])
            low[x] = min(low[x],dfn[e[i].y]);
    }
    if(dfn[x] == low[x]){
        con++;
        do{
            strong[stack[--ts]] = con;
            instack[stack[ts]] = 0;
        }while(x != stack[ts]);
    }
}
int relax(int x,int y,int w){
    if(dis[y] > dis[x]+w){
        dis[y] = dis[x]+w;
        return 1;
    }
    return 0;
}
int spfa(int s,int t,int n){
    int i,now,used[N];
    queue<int> q;
    clc(used,0);
    for(i = 1;i<=n;i++)
        dis[i] = INF;
    dis[s] = 0;
    used[s] = 1;
    q.push(s);
    while(!q.empty()){
        now = q.front();
        q.pop();
        used[now] = 0;
        for(i = f2[now];i!=-1;i=h[i].next)
            if(relax(now,h[i].y,h[i].w) && !used[h[i].y]){
                used[h[i].y] = 1;
                q.push(h[i].y);
            }
    }
    return dis[t];
}
int main(){
    while(scanf("%d %d",&n,&m) && (n+m)){
        int i,j,a,b,w;
        init();
        for(i = 1;i<=m;i++){
            scanf("%d %d %d",&a,&b,&w);
            add(a,b,w);
        }
        for(i = 1;i<=n;i++)
            if(dfn[i] == -1)
                tarjan(i);
        for(i = 1;i<=n;i++)
            for(j = first[i];j!=-1;j=e[j].next)
                if(strong[i] != strong[e[j].y])
                    add2(strong[i],strong[e[j].y],e[j].w);
        scanf("%d",&w);
        while(w--){
            scanf("%d %d",&a,&b);
            j = spfa(strong[a],strong[b],con);
            if(j != INF)
                printf("%d\n",j);
            else
                printf("Nao e possivel entregar a carta\n");
        }
        putchar('\n');
    }
    return 0;
}


你可能感兴趣的:(poj 3114 强连通分量+最短路(间谍通信))