【NOIP2014提高组T5】寻找道路-双spfa
(本人本题完成于2016-7-19)
题目大意:给一个有向图,每条边的权值为1,求是否存在一条从点s到点t的路径使得路径上的每一个点的出边所指的点都存在到t的路径,如果有,输出最短路径长度,否则输出-1。
做法:由于点数N很大(可达10000),所以要用邻接表存储有向图。在读入边时,顺便存一个反图,对该反图以t为起点做一遍spfa,找到不存在到t的路径的点(即在反图中由t不能到达的点),再将这些点和反图中这些点所指向的点(即正图中有出边指向这些点的点)标记上,再对正图以s为起点做一遍spfa,避开标记过的点(即不能在路径中出现的点),就能得出答案了。
以下是本人代码:
#include <cstdio>
#include <cstdlib>
#include <iostream>
#include <algorithm>
#include <queue>
#define inf 99999999
using namespace std;
int n,m,s,t,total=0,last[10010]={0},flast[10010]={0};
bool vi[10010]={0};
struct edge
{
long v,next;
}g[220010],fg[220010]; //数组模拟链表,用邻接表存储有向图,g为正图,fg为反图
void readg(int i,int j) //往正图中加入边(i,j)
{
g[last[i]].next=total;
g[total].v=j;
g[total].next=0;
last[i]=total;
}
void readfg(int i,int j) //往反图中加入边(i,j)
{
fg[flast[i]].next=total;
fg[total].v=j;
fg[total].next=0;
flast[i]=total;
}
void input()
{
scanf("%d %d",&n,&m);
for(int i=1;i<=n;i++)
{
last[i]=i;
flast[i]=i;
g[i].next=0;g[i].v=i;
fg[i].next=0;fg[i].v=i;
total++;
}
for(int i=1;i<=m;i++)
{
int x,y;
scanf("%d %d",&x,&y);
total++;
readg(x,y);
readfg(y,x);
}
scanf("%d %d",&s,&t);
}
void fspfa()
{
bool d[10010]={0};
queue <int> q;
q.push(t);d[t]=1;
while(!q.empty())
{
int h=q.front(),i;
q.pop();
i=h;
while(i!=0)
{
if (!d[fg[i].v]) {q.push(fg[i].v);d[fg[i].v]=1;} //如果该点没访问过,将其插入队列
i=fg[i].next;
}
}
for(int i=1;i<=n;i++) //标记路径中不能包含的点
if (!d[i])
{
int j=i;
while(j!=0)
{
vi[fg[j].v]=1;
j=fg[j].next;
}
}
}
void spfa()
{
int d[10010];
queue <int> q;
q.push(s);d[s]=0;
if (vi[s]) {printf("-1");return;}
for(int i=1;i<=n;i++) if (i!=s) d[i]=inf;
while(!q.empty())
{
int h=q.front(),i;
q.pop();
i=h;
while(i!=0)
{
if (!vi[g[i].v]&&d[h]+1<d[g[i].v]) {q.push(g[i].v);d[g[i].v]=d[h]+1;} //做spfa时避开不能包含的点
i=g[i].next;
}
}
if (d[t]!=inf) printf("%d",d[t]);
else printf("-1");
}
int main()
{
input();
fspfa();
spfa();
return 0;
}