题意:给定一个有向带权图,随后给出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; }