NC16498 [NOIP2014]寻找道路

题目链接

题目

题目描述

在有向图G中,每条边的长度均为1,现给定起点和终点,请你在图中找一条从起点到终点的路径,该路径满足以下条件:

1.路径上的所有点的出边所指向的点都直接或间接与终点连通。

2.在满足条件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

说明

img

如上图所示,箭头表示有向道路,圆点表示城市。起点1与终点3不连通,所以满足题目描述的路径不存在,故输出-1。

示例2

输入

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

输出

3

说明

img

如上图所示,满足条件的路径为1->3->4->5。注意点2不能在答案路径中,因为点2连了一条边到点6,而点6不与终点5连通。

备注

对于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。

题解

知识点:BFS,图论。

显然用bfs,但与普通走迷宫不同的是,迷宫路径上的点的任意邻接点都必须也能通往终点,这意味着最短路径并非合法路径。

首先筛选出哪些点能通往终点,选择反向建图,从终点出发走迷宫走到的点都是可达终点的点,用 \(ck1[i]\) 表示编号 \(i\) 的点可以到达终点。

然后遍历每个点,如果是不可行点,将其在反向图意义下的邻接点全都排除。因为图是反向的我们无法找到正向图意义下的邻接点,从而无法从一个可达点找其邻接点是否不可达,因此反过来,把不可达点的反向邻接点都排除即可,因为不可达点是反向邻接点的正向邻接点。用 \(ck2[i]\) 表达这个点是否是满足条件的点。

随后在 \(ck2\) 的限制下遍历图找最短路即可。

时间复杂度 \(O(n+m)\)

空间复杂度 \(O(n+m)\)

代码

#include <bits/stdc++.h>

using namespace std;

vector<int> g[10007];
bool ck1[10007], ck2[10007];
int step[10007];

void bfsck(int st) {
    queue<int> q;
    q.push(st);
    ck1[st] = 1;
    while (!q.empty()) {
        int cur = q.front();
        q.pop();
        for (int i = 0;i < g[cur].size();i++) {
            int now = g[cur][i];
            if (ck1[now]) continue;
            ck1[now] = 1;
            q.push(now);
        }
    }
}

int bfs(int st, int ed) {
    queue<int> q;
    q.push(st);
    step[st] = 0;
    ck2[st] = 0;
    while (!q.empty()) {
        int cur = q.front();
        q.pop();
        if (cur == ed) return step[ed];
        for (int i = 0;i < g[cur].size();i++) {
            int now = g[cur][i];
            if (!ck2[now]) continue;
            ck2[now] = 0;
            step[now] = step[cur] + 1;
            q.push(now);
        }
    }
    return -1;
}

int main() {
    std::ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
    int n, m;
    cin >> n >> m;
    for (int i = 1;i <= m;i++) {
        int u, v;
        cin >> u >> v;
        if (u == v) continue;
        g[v].push_back(u);
    }
    int st, ed;
    cin >> st >> ed;

    bfsck(ed);///找到终点可达点
    memcpy(ck2, ck1, sizeof(ck1));///复制出来

    for (int u = 1;u <= n;u++) {///进一步筛选所有正向邻接点是终点可达点的终点可达点
        ///因为通路反向了,不能通过找到终点可达点找到判断其正向邻接点是否是终点不可达点
        ///而要通过终点不可达点排除其反向邻接点,因为终点不可达点的反向邻接点的正向邻接点是终点不可达点
        if (ck1[u]) continue;
        for (int j = 0;j < g[u].size();j++)
            ck2[g[u][j]] = 0;
    }
    cout << bfs(ed, st) << '\n';///找到终点到起点的最短路径
    return 0;
}
posted @ 2022-07-16 16:34  空白菌  阅读(79)  评论(0编辑  收藏  举报