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