【noip】寻找道路 DFS SPFA

2 篇文章 0 订阅

还是14年noip,这是day2第二题。

寻找道路

描述

在有向图 G 中,每条边的长度均为 1,现给定起点和终点,请你在图中找一条从起点到 终点的路径,该路径满足以下条件:
路径上的所有点的出边所指向的点都直接或间接与终点连通。
在满足条件 1 的情况下使路径最短。
注意:图 G 中可能存在重边和自环,题目保证终点没有出边。 请你输出符合条件的路径的长度。

输入格式

第一行有两个用一个空格隔开的整数 n 和 m,表示图有 n 个点和 m 条边。
接下来的 m 行每行 2 个整数 x、y,之间用一个空格隔开,表示有一条边从点 x 指向点y。
最后一行有两个用一个空格隔开的整数 s、t,表示起点为 s,终点为 t。

输出格式

输出只有一行,包含一个整数,表示满足题目描述的最短路径的长度。
如果这样的路径不存在,输出-1。

样例

样例输入1

3 2
1 2
2 1
1 3

样例输出1

-1

样例输入2

6 6
1 2
1 3
2 6
2 5
4 5
3 4
1 5

样例输出2

3

限制

对于 30%的数据,0 < n ≤ 10,0 < m ≤ 20;
对于 60%的数据,0 < n ≤ 100,0 < m ≤ 2000;
对于 100%的数据,0 < n ≤ 10,000,0 < m ≤ 200,000,0 < x,y,s,t ≤ n,x ≠ t。

提示

【输入输出样例1说明】
这里写图片描述
如上图所示,箭头表示有向道路,圆点表示城市。起点 1 与终点 3 不连通,所以满足题目描述的路径不存在,故输出-1。
【输入输出样例2说明】
这里写图片描述
如上图所示,满足条件的路径为 1->3->4->5。注意点 2 不能在答案路径中,因为点 2 连了一条边到点 6,而点 6 不与终点 5 连通。

来源

NOIP2014 提高组 Day2
图片来源vijos

这道题是day2的图论题,day1的图论我一分没得,day2结果我也只得了10分,萎得不行,这道题不难,也不太会超时,思想比较简单,我也就简单地说一下吧。
首先就是筛点,把所有可以选的点筛出来,然后再在筛出来的点上面跑SPFA,找到S和T之间的最短路就可以了。
筛点可以先存一个反向图,再从T点用bfs,判断每个点能不能到T,然后在判断是不是所用它连接的点也可以到T,这里可以用入度,每次找到,入度减一,如果最后入度为0,那么就可以选这个点,不为0就要筛出去。
筛点后直接SPFA,比较简单这里不再赘述。
话不多说,代码见下

#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
#define M 200005
#define N 10005
#define INF (1<<30)

using namespace std;

int first[N],next[M],to[M],flag[M],t,can[N];
int first2[N],next2[M],to2[M],t2;
int n,m,S,T,d,l[N],b[N],vis[N],c[N];
void addedge(int s,int v)
{
    next[++t]=first[s];
    first[s]=t;
    to[t]=v;

}

void addedge2(int s,int v)
{
    next2[++t2]=first2[s];
    first2[s]=t;
    to2[t]=v;
    c[v]++;
}

void dfs(int x)
{
    if(vis[x])return ;
    vis[x]=1;
    if(!first2[x])return ;
    for(int i=first2[x];i!=0;i=next2[i])
    {
        int v=to2[i];
        c[v]--;
        dfs(v);
    }
}

void SPFA()
{
    queue<int>Q;
    while(!Q.empty())Q.pop();
    l[S]=0;
    Q.push(S);
    while(!Q.empty())
    {
        int s=Q.front();Q.pop();
        for(int i=first[s];i!=0;i=next[i])
        {

            int v=to[i];
            if(!can[v])continue;
            if(l[v]>l[s]+1)
            {
                l[v]=l[s]+1;
                Q.push(v);
            }
        }
    }
}

int main()
{
    cin>>n>>m;
    for(int i=1;i<=m;i++)
    {
        int s,t;
        scanf("%d%d",&s,&t);
        if(s!=t)addedge(s,t);
        if(s!=t)addedge2(t,s);
    }
    cin>>S>>T;
    dfs(T);
    for(int i=1;i<=n;i++)
    if(!c[i]&&vis[i])can[i]=1;
    for(int i=1;i<=n;i++)l[i]=INF;
    SPFA();
    int ans;
    if(l[T]>=INF)ans=-1;
    else ans=l[T];
    cout<<ans;
    return 0;
}

如果有什么问题,或错误,请在评论区提出,谢谢。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值