P2296 寻找道路 题解
题解
首先感谢那些告诉我逆向思维的大佬们,这个题我也的确是用的逆向思维,但是跟其他大佬们的方法还是有一定差别的。(才不会告诉你我用其他题解的方法写挂了)。
我们可以这样理解题意,现在所有点都被他的出边束缚住了,有几个出边,就有几层束缚,而我们为了解救他们,从反向边往回广搜,如果遇到一个点,就使其的束缚减少一层,这样的话,最后没有束缚的点就是我们在最后求最短路时可以经过的点。那我们就可以用出度记录束缚(有没有发现像拓扑排序?)
所以我们可以这样做:
方法
- 我们可以先统计原图中每个点的出度。
- 反向搜索,并减少束缚。(注意:这里要记录每个边只能有一次减少束缚的机会,不然在广搜的时候,很有可能一条边会被搜到多次,从而导致答案错误)
- 判断哪些点可以走
- 跑最短路
代码:
ps:(将代码里无关紧要的代码进行了压行,如有不适,请谅解)
#include <bits/stdc++.h>
using namespace std;
struct edge {
int from, to, nex;
} e[200100], ef[200010];
int n, m, s, t, a, b, cnt, out_degree[10010], lin[10010], linf[10010], check[10100], vis[10100], dis[10010], flag[200010];
inline void add(int a, int b)
{
e[++cnt].from = a; e[cnt].to = b; e[cnt].nex = lin[a]; lin[a] = cnt;
ef[cnt].from = b; ef[cnt].to = a; ef[cnt].nex = linf[b]; linf[b] = cnt;
out_degree[a]++;
}
inline void init()
{
scanf("%d%d", &n, &m);
for (int i = 1; i <= m; i++)
{
int a, b;
scanf("%d%d", &a, &b);
add(a, b);
}
scanf("%d%d", &s, &t);
}
inline void bfs()
{
queue <int> q;
q.push(t);
while(!q.empty())
{
int cur = q.front(); q.pop();
for (int i = linf[cur]; i; i = ef[i].nex)
{
if (flag[i]) continue; flag[i] = 1;//坑:一条边只能减少一次
if (!out_degree[ef[i].to])//坑:可能会导致程序崩溃
continue;
out_degree[ef[i].to]--;
q.push(ef[i].to);
}
}
}
inline void spfa()//死掉的算法
{
for (int i = 1; i <= n; i++)
dis[i] = 2147483647;
queue <int> q;
q.push(s);
dis[s] = 0;
while (!q.empty())
{
int cur = q.front();
q.pop();
vis[cur] = 0;
for (int i = lin[cur]; i; i = e[i].nex)
if (check[e[i].to] && dis[cur] + 1 < dis[e[i].to])
{
dis[e[i].to] = dis[cur] + 1;
if (!vis[e[i].to])
vis[e[i].to] = 1, q.push(e[i].to);
}
}
}
int main()
{
init();
bfs();
for (int i = 1; i <= n; i++)//判断束缚是否已经消失
if (!out_degree[i])
check[i] = 1;
spfa();
printf("%d", dis[t]);
}