http://icpc.upc.edu.cn/problem.php?cid=1687&pid=4
已知只有所有出边都直接或间接指向终点的点才可能被选择,所以就建反边,从终点想起点扫,dfs,bfs均可,在把所有终点不能达到的点打上标记,这些点均不可备选择,并且在反边图中这些点所指向的点也不能被选择(因为在正边图中这些点指向标记点)。因此可以删去图中不符合要求的点,然后跑最短路即可。
#include<cstdio>
#include<cstring>
#include<queue>
using namespace std;
struct str
{
int t,n;
} e[3000010];
struct str1
{
int t,n;
} e1[3000010];
int n,m,t,tt,i,j,k,s,c[2000005],vv[3000000],t1,can[2000005],S,T
,h[3000005],b[3000005],h1[3000005],v[2000005];
void add(int u,int v)
{
t++;
e[t].t=v;
e[t].n=h[u];
h[u]=t;
}
void add1(int u,int v)
{
tt++;
e1[tt].t=v;
e1[tt].n=h1[u];
h1[u]=tt;
}
void bfs(int x)
{
queue<int>q;
q.push(x);
v[x]=1;
while (q.size())
{
int k=q.front();
q.pop();
for (int i=h1[k]; i; i=e1[i].n)
{
int kk=e1[i].t;
if (v[kk]==0)
{
q.push(kk);
v[kk]=1;
}
}
}
}
void spfa()
{
queue<int>q;
b[S]=0;
q.push(S);
while(q.size())
{
int k=q.front();
q.pop();
for(int i=h[k]; i; i=e[i].n)
{
int kk=e[i].t;
if(!v[kk]||vv[kk]==1)
continue;
if(b[kk]>b[k]+1)
{
b[kk]=b[k]+1;
q.push(kk);
}
}
}
}
int main()
{
scanf("%d%d",&n,&m);
for(i=1; i<=m; i++)
{
scanf("%d%d",&s,&t1);
if(s!=t1)
{
add(s,t1);
add1(t1,s);
}
}
scanf("%d%d",&S,&T);
bfs(T);
for (i=1; i<=n; i++)
if (v[i]==0)
for (j=h1[i]; j; j=e1[j].n)
vv[e1[j].t]=1;
for (i=1; i<=n; i++)
b[i]=1000000000;
spfa();
if(b[T]>=1000000000)
printf("-1\n");
else
printf("%d\n",b[T]);
}