您的位置:首页 > 其它

【NOIP2014提高组T5】寻找道路-双spfa

2016-07-19 19:07 381 查看
(本人本题完成于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;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: