[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;
} 
posted @ 2021-07-30 14:56  行舟C  阅读(46)  评论(1)    收藏  举报