搜索:寻找道路
题目链接: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;
}