【NOIP 2014 Day2 T2】寻找道路(BFS)

本文介绍了一种使用BFS寻找有向图中满足特定条件的最短路径的方法。首先通过反向BFS标记从终点可达的点,然后从起点再次进行BFS搜索最短路径。在实现过程中需要注意避免重复边的影响,并确保正确清除中间状态。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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

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

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

样例 Sample
Input1
3 2
1 2
2 1
1 3
Output1
-1

这里写图片描述

Input2
6 6
1 2
1 3
2 6
2 5
4 5
3 4
1 5
Output2
3

这里写图片描述

数据范围及提示 Data Size & Hint
对于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,所以可以用BFS来做。先反向建边跑一遍BFS,把能从终点到达的点标记出来,然后重新建边,从起点再跑一遍BFS找最短路。不要忘记把第一次BFS时用的数组清零。
PS:这题我偷懒想跑两遍spfa,然后。。。MDZZ

这里写图片描述

代码如下

#include<queue> 
#include<cstdio>
#include<cstring>
#include<iostream>
using namespace std;
const int MAXE=200010;
const int MAXV=10010;
int first[MAXV],nxt[MAXE<<1],d[MAXV];
int x[MAXE<<1],y[MAXE<<1],a[MAXV];
bool used[MAXV];
int n,m,tot,s,t;
struct edge
{
    int from,to;
}es[MAXE<<1];
void build(int f,int t)
{
    es[++tot]=(edge){f,t};
    nxt[tot]=first[f];
    first[f]=tot;
}
void init1()//第一次bfs初始化
{
    memset(first,-1,sizeof(first));
    memset(used,0,sizeof(used));
    tot=0;
}
void init2()//第二次bfs初始化
{
    memset(first,-1,sizeof(first));
    memset(es,0,sizeof(es));
    memset(d,-1,sizeof(d));
    memset(a,0,sizeof(a));
    tot=0;
}
bool check(int ch)//检查一个点是否能走
{
    for(int i=first[ch];i!=-1;i=nxt[i])
    {
        int v=es[i].to;
        if(!used[v]) return false;
        //只要这个点连着任何一个不与终点连通的点,这个点就不能用
    }
    return true;
}
void bfs1()//第一次bfs
{
    int ww=1,tt=0;
    a[1]=t;
    used[t]=1;
    while(ww>tt)
    {
        tt++;
        for(int i=first[a[tt]];i!=-1;i=nxt[i])
        {
            int v=es[i].to;
            if(!used[v])
            {
                ww++;
                a[ww]=v;
                used[v]=1;//标记一下能从终点到达的点
            }
        }
    }
}
bool bfs2()
{
    int ww=1,tt=0;
    d[s]=0;
    a[1]=s;
    while(ww>tt)
    {
        tt++;
        if(!check(a[tt])) continue;//如果当前点不能用直接continue
        for(int i=first[a[tt]];i!=-1;i=nxt[i])
        {
            int v=es[i].to;
            if(d[v]==-1)
            {
                d[v]=d[a[tt]]+1;
                ww++;
                a[ww]=v;
                if(es[i].to==t)
                {
                    printf("%d",d[v]);
                    return true;
                }
            }
        }
    }
    return false;
}
int main()
{
    scanf("%d%d",&n,&m);
    init1();
    for(int i=1;i<=m;i++)
    {
        scanf("%d%d",&x[i],&y[i]);
        build(y[i],x[i]);//先反向建边
    }
    scanf("%d%d",&s,&t);
    bfs1();
    init2();
    for(int i=1;i<=m;i++) build(x[i],y[i]);//正向建边,不要忘了初始化
    if(!used[s])
    {
        printf("-1\n");
        return 0;
    }
    if(!bfs2()) printf("-1\n");
    return 0;
}
/*
6 6
1 2
1 3
2 5
2 6
3 4
4 5
1 5
*/
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值