寻找道路


题目链接: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代码


```cpp

include

include

include

include

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 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 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;

}


文章作者: Kong Aobo
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 Kong Aobo !
  目录