题解 P2296 【寻找道路】
对一般反向边的做法进行补充。
1.题目的意思是,如果一个点所能到达的点中,有一个点完全无法通过任何路径到达终点,那么这个点不能存在于最短路径上。
于是使用标记数组,三种状态,0为可以到达,-1为完全无法到达,1为不能存在于最短路径上。显然只有值为0的点才可能存在于最短路径上。
2.关于效率问题,刚开始用的是STL结果超时,除了vector全改成手打还是超时,结果发现是忘记判BFS的标记(导致RE与TLE)...因为边权为1,所以直接使用BFS即可,效率更高一点,为O(n)。
3.反向边如何写的问题,结构体两个vector型邻接表,算是空间换时间而且也不算很复杂。
对反向边的做法进行说明:
看代码吧XD
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<vector>
#include<queue>
#include<cstring>
using namespace std;
const int N=10010;
int gi(){
int x=0;
char ch=getchar();
while(ch<'0'||ch>'9')ch=getchar();
while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();
return x;
} //读入优化
struct Graph{
int glen[N];
vector<int> to[N];
}G1,G2;//两个邻接表
void insm(Graph &g,int a,int b){
g.glen[a]++;
g.to[a].push_back(b);
}//邻接表通用处理函数
struct Que{
int a,len;
}qb[N];
int head,tail,qlen;
inline void insq(Que x){
qb[tail++]=x;
tail%=N,qlen++;
}
inline Que delq(){
Que ret=qb[head++];
head%=N,qlen--;
return ret;
}//队列之二
int tag[N];//标记点,-1为不可至,1为不可走,0为正常
bool tagb[N];//队列标记
int main(){
int n,m;
scanf("%d%d",&n,&m);
int a,b;
for(int i=0;i<m;i++){
a=gi(),b=gi();
insm(G1,a,b),insm(G2,b,a);
}
int s,t;
scanf("%d%d",&s,&t);
memset(tag,-1,sizeof(tag));
//第一层BFS
int qu[N];//队列之一,qlen和tail等先用着,之后清零
qu[tail++]=t,qlen++,tail%=N;
tag[t]=0;
while(qlen){
a=qu[head++],qlen--,head%=N;
for(int i=0;i<G2.glen[a];i++){
if(!tagb[G2.to[a][i]]){
tagb[G2.to[a][i]]=1;
tag[G2.to[a][i]]=0;
qu[tail++]=G2.to[a][i],qlen++,tail%=N;
}
}
}
//处理类型点1
for(int i=1;i<=n;i++){
if(tag[i]<0){
for(int j=0;j<G2.glen[i];j++)
tag[G2.to[i][j]]=1;
}
}
//第二层BFS
qlen=head=tail=0;
memset(tagb,0,sizeof(tagb));//队列之二初始化
Que c;
c.a=s,c.len=0;
insq(c);
while(qlen){
c=delq();
for(int i=0;i<G1.glen[c.a];i++){
if(!tag[G1.to[c.a][i]]&&!tagb[G1.to[c.a][i]]){
tagb[G1.to[c.a][i]]=1;
Que d=c;
d.a=G1.to[c.a][i];
d.len++;
if(d.a==t){
cout<<d.len<<endl;
return 0;
}
insq(d);
}
}
}
cout<<-1<<endl;
return 0;
}