[NOIP2014]寻找道路
一、前言
想来想去还是感觉得写题解来记录一下自己的写题经历,不然很多题做过了就只是做过,可能还没有理解透彻,所以就有了这第一篇题解(代码有些冗余,大佬们见谅),以后也继续加油!!!
二、题目描述
题目链接:NC16498
在有向图G中,每条边的长度均为1,现给定起点和终点,请你在图中找一条从起点到终点的路径,该路径满足以下条件:
1.路径上的所有点的出边所指向的点都直接或间接与终点连通。
2.在满足条件1的情况下使路径最短。
注意:图G中可能存在重边和自环,题目保证终点没有出边。
请你输出符合条件的路径的长度。
输入描述:
第一行有两个用一个空格隔开的整数n和m,表示图有n个点和m条边。
接下来的m行每行2个整数x、y,之间用一个空格隔开,表示有一条边从点x指向点y。
最后一行有两个用一个空格隔开的整数s、t,表示起点为s,终点为t。
输出描述:
输出只有一行,包含一个整数,表示满足题目描述的最短路径的长度。如果这样的路径不存在,输出-1。
三、思路
1.因为题目要求路径上的点的出边所指向的点都必须与终点连通,所以可以做一遍反向bfs,以终点作为起点,来确定有多少条路径是合法的,并将路径上的点标记下来
2.标记完后,枚举所有点的每条出边,如果出边指向的点是合法的,那么指向的这个点实际上是不合法的(因为现在还是反向)
3.正向bfs一遍,所得到的起点到终点的距离即为答案
PS: 值得注意的是,在枚举判断每条出边的时候,需要copy一份原st数组,因为一直在原st数组里判断会将原本是合法的点也删除(比如一个点开始被标记,它通过一个序号比它小的点删除了,那么访问到它的时候,就会被当成开始就没被标记的点,会通过它把合法点删除)
四、代码
#include<bits/stdc++.h>
using namespace std;
typedef pair<int, int> PII;
const int N = 100100, M = 200020;
int h[M], e[M], ne[M], idx;//正向bfs需要用到的邻接表
int hb[M], eb[M], neb[M], idxb;//反向bfs需要用到的邻接表
int w[M];//这里权值没用
int wb[M];
int dist[M];
bool st[N], er[N];//判断数组
int n, m;
int sta, edl;
bool fl[N];//判断是否出现过的数组
void add(int x, int y, int c){
w[idx] = c; e[idx] = y; ne[idx] = h[x]; h[x] = idx ++;
}
void addb(int x, int y, int c){
wb[idxb] = c; eb[idxb] = y; neb[idxb] = hb[x]; hb[x] = idxb ++;
}
//反向bfs找合法点
void bfsb(){
queue<int> q;
q.push(edl);
memset(fl, false, sizeof fl);
while(q.size()){
int t = q.front();
q.pop();
for(int i = hb[t]; i != -1; i = neb[i]){
if(fl[eb[i]] == false){
fl[eb[i]] = true;
st[eb[i]] = true;
//cout << eb[i] << endl;
q.push(eb[i]);
}
}
}
//开始枚举每个点的出边
memcpy(er, st, sizeof st);
for(int i = 1; i <= n; i ++){
if(st[i] || i == edl) continue;
for(int j = hb[i]; j != -1; j = neb[j])
if(st[eb[j]] == true){
er[eb[j]] = false;
}
}
}
//正向bfs找最短距离
void bfs(){
queue<int> q;
q.push(sta);
dist[sta] = 0;
memset(fl, false, sizeof fl);
while(q.size()){
int t = q.front();
q.pop();
for(int i = h[t]; i != -1; i = ne[i]){
int j = e[i];
if(er[j] && fl[j] == false || j == edl){
dist[j] = dist[t] + 1;
q.push(j);
fl[j] = true;
}
}
}
}
int main(){
memset(dist, -1, sizeof dist);
cin >> n >> m;
int u, v;
memset(h, -1, sizeof h);
memset(hb, -1, sizeof hb);
for(int i = 0; i < m; i ++){
cin >> u >> v;
add(u, v, 1);
addb(v, u, 1);
}
cin >> sta >> edl;
bfsb();
bfs();
cout << dist[edl] << endl;
return 0;
}

浙公网安备 33010602011771号