Loading

搜索:寻找道路

题目链接:https://ac.nowcoder.com/acm/problem/16498

题目描述


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

输入描述


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

输出描述


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

示例


输入

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

输出

3

分析


本题的关键为条件1,如何才能得知一个点是否与终点连通呢?正向思考较为棘手,反向就容易许多,以终点为起点bfs就可以解决这一问题。
得知哪些点不与终点连通后,将这些点和被这些点波及的点从图中remove掉,正向bfs即可。
有一些细节需要注意,见代码注释。

AC代码


#include <string>
#include <iostream>
#include <cstring>
#include <queue>
using namespace std;

#define maxn 10000
int n, m;
int s, t;
bool graph[maxn][maxn];
int dis[maxn];

void remove(int point) //将一个点及其相关边剔除
{
    for (int i = 0; i < n; i++)
        graph[i][point] = 0;
    for (int j = 0; j < n; j++)
        graph[point][j] = 0;
}

void bfs1() //正向处理
{
    queue<int> q;
    q.push(s - 1);
    dis[s - 1] = 0;
    while (!q.empty())
    {
        int a = q.front();
        q.pop();
        for (int i = 0; i < n; i++)
        {
            if (!dis[i] && graph[a][i])
            {
                dis[i] = dis[a] + 1;
                q.push(i);
            }
        }
    }
}

void bfs2() //反向处理
{
    queue<int> q;
    q.push(t - 1);
    dis[t - 1] = 0;
    while (!q.empty())
    {
        int a = q.front();
        q.pop();
        for (int i = 0; i < n; i++)
        {
            if (!dis[i] && graph[i][a])
            {
                dis[i] = dis[a] + 1;
                q.push(i);
            }
        }
    }
}

int main()
{
    memset(graph, 0, sizeof(graph));
    memset(dis, 0, sizeof(dis));

    cin >> n >> m;
    while (m--)
    {
        int x, y;
        cin >> x >> y;
        if (x == y)
            continue; //舍弃自环
        graph[x - 1][y - 1] = 1;
    }
    cin >> s >> t;
    bfs2();
    dis[t - 1] = 1; //终点是起点 不能被剔除
    for (int i = 0; i < n; i++)
    {
        if (!dis[i])
        {
            for (int j = 0; j < n; j++) //注意remove的先后顺序
            {                           //“波及”关系是需要原来点的边的数据的 
                if (graph[j][i])        //所以先清除被“波及”点再处理原来的点
                    remove(j);
            }
            remove(i);
        }
    }

    memset(dis, 0, sizeof(dis));
    bfs1();
    if (dis[t - 1])
        cout << dis[t - 1] << endl;
    else
        cout << -1 << endl;
    return 0;
}
posted @ 2021-03-12 15:15  Kong-Aobo  阅读(51)  评论(0编辑  收藏  举报