洛谷P2296题解
原题
思路概述
题意分析
给定包含 \(n\) 个点和 \(m\) 条有向边的图 \(G(V,E)\),求从起点 \(s\) 到终点 \(t\) 的最短合法路径。一条路径是合法路径当且仅当路径上所有点出边的顶点与终点 \(t\) 连通。
思路简述
考虑到对于合法路径的定义较为复杂,需要先在图中筛选出合法路径上的“合法点”。
由于一个合法点的条件是其所有出边的顶点与终点连通,而本题输入的是有向图,所以考虑先反向建图,标记出原图中所有能与终点连通的点,再判定每一个点是否是合法点(即该点与其出边的顶点能否与终点连通)。
判定出合法点后用广搜处理最短路即可。
算法实现
关于建图
由于本题需要建正反向两个图,所以笔者建议将图封装成结构体,连边时调用内部函数即可。
关于广搜求最短路
需要一个辅助数组记录当前所有点到起点的距离。
由于广搜是多条路径同时搜索,所以当前搜索到的点与起点的距离必然是最短路径长度;相应地,若一个点被搜索到时其最短路长度已经被更新,就说明该点不需要再被考虑。
其他注意事项
输入数据存在重边和自环,输入时需要判定。
AC code
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<cmath>
#include<cstring>
#include<queue>
#include<set>
#include<ctime>
#include<map>
#define RI register int
using namespace std;
const int maxn=2e5+10;
typedef struct
{
int u,nex;
}side;
typedef struct
{
side s[maxn];
int cnt;
int fir[maxn];
inline void add(int x,int y){s[++cnt]=(side){y,fir[x]};fir[x]=cnt;return;}
}graph;
graph g,rg;
int n,m,s,t;
int dis[maxn];
bool avl[maxn],rec[maxn];
map<pair<int,int>,bool> mp;
inline void bfs_rvr(void);
inline void bfs(void);
int main()
{
ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
cin >> n >> m;
for(RI i=1,x,y;i<=m;++i)
{
cin >> x >> y;
if(x!=y && !mp[make_pair(x,y)])/*判下重边和自环*/
{
g.add(x,y);rg.add(y,x);
mp[make_pair(x,y)]=1;
}
}
cin >> s >> t;
bfs_rvr();/*筛选合法点*/
if(!avl[s]) puts("-1");
else
{
bfs();/*广搜求最短路*/
printf("%d",dis[t]);
}
fclose(stdin);fclose(stdout);
return 0;
}
inline void bfs_rvr(void)
{
queue<int> q;
q.push(t);avl[t]=1;
for(;!q.empty();)
{
RI temp=q.front();q.pop();
for(RI i=rg.fir[temp];i;i=rg.s[i].nex)
if(!avl[rg.s[i].u])
{
avl[rg.s[i].u]=1;
q.push(rg.s[i].u);
}
}
if(!avl[s]) return;
else
{
for(RI i=1;i<=n;++i) rec[i]=1;
for(RI i=1;i<=n;++i)
for(RI j=g.fir[i];j;j=g.s[j].nex)
if(!avl[g.s[j].u])
{
rec[i]=0;
break;
}
}
return;
}
inline void bfs(void)
{
queue<int> q;q.push(s);
for(;!q.empty();)
{
RI temp=q.front();q.pop();
if(temp==t) break;
else
for(RI i=g.fir[temp];i;i=g.s[i].nex)
if(rec[g.s[i].u] && !dis[g.s[i].u])
{
dis[g.s[i].u]=dis[temp]+1;
q.push(g.s[i].u);
}
}
return;
}