寻找道路——呆滞大佬der最骚暴力

寻找道路

题目描述:

有点的出边所指向的点都直接或间接与终点连通。

2 .在满足条件1 的情况下使路径最短。

注意:图G 中可能存在重边和自环,题目保证终点没有出边。

请你输出符合条件的路径的长度。

输入格式:

第一行有两个用一个空格隔开的整数n 和m ,表示图有n 个点和m 条边。

接下来的m 行每行2 个整数x 、y ,之间用一个空格隔开,表示有一条边从点x 指向点y 。

最后一行有两个用一个空格隔开的整数s 、t ,表示起点为s ,终点为t 。

输出格式:

输出只有一行,包含一个整数,表示满足题目᧿述的最短路径的长度。如果这样的路径不存在,输出- 1 。

样例输入:

样例1:
3 2  
1 2  
2 1  
1 3  

样例2:
6 6  
1 2  
1 3  
2 6  
2 5  
4 5  
3 4  
1 5  

样例输出:

样例1:
-1

样例2:
3

提示:

解释1:

1498615625157677374.png

如上图所示,箭头表示有向道路,圆点表示城市。起点1 与终点3 不连通,所以满足题

目᧿述的路径不存在,故输出- 1 。

解释2:

1498615654268418398.png

如上图所示,满足条件的路径为1 - >3- >4- >5。注意点2 不能在答案路径中,因为点2连了一条边到点6 ,而点6 不与终点5 连通。

对于30%的数据,0

对于60%的数据,0

对于100%的数据,0

时间限制:1000ms
空间限制:256MByte

来源:NOIP2014提高t5

俗话说的好啊,现有搜索后有天,反向剪枝日神仙。这题在网上的题解十分多我看了一下,有用栈优化bellman-ford,SPFA,Dijkstra+heap,bellman-ford+stack等等等等,这些我都听不懂。呆滞大佬说,我只要两次bfs就可以搞定。关于程序中诡异的ti数组和a结构体,详见《联合权值——呆滞大佬der最骚操作?》

http://www.cnblogs.com/cain-/p/7306003.html

#include<bits/stdc++.h>
#include<bits/stdc++.h>
using namespace std;
struct nob{
	int sta,ed;
}a[200005];
bool mmp(nob a,nob b){
	return a.ed<b.ed;
}
int n,m,has[100005]={0},has1[100005]={0},s[1000001]={0},ti[200005]={0},start,finish,step[200005]={0};
int main(){
	cin>>n>>m;
	for (int i=1; i<=m; i++){
		cin>>a[i].sta>>a[i].ed;
		ti[a[i].ed]++;
	}//这里是不是觉得有点奇怪
	for (int i=1; i<=n; i++) ti[i]+=ti[i-1];
	cin>>start>>finish;
	sort(a+1,a+1+m,mmp);
	int head=0,tail=1;
	s[1]=finish;//从尾巴开始找
	has[finish]=1;
	while (head<tail){
		head++;
		for (int i=ti[s[head]-1]+1; i<=ti[s[head]]; i++){
			if (has[a[i].sta]==0){
				has[a[i].sta]=1;
				tail++;
				s[tail]=a[i].sta;
			}
		}
	}//找出不能到达的点
	for (int i=1; i<=n; i++){
		if (has[i]==0){
			has1[i]=1;
			for (int l=ti[i-1]+1; l<=ti[i]; l++){
				has1[a[l].sta]=1;
			}
		}
	}//用不能到达的点来反向标记他的父亲,表示父亲节点有子节点没有直接或间接地指向终点
	head=0;tail=1;
	s[1]=finish;
	has1[finish]=1;
	while (head<tail){
		head++;
		if (s[head]==start){
			cout<<step[s[head]];
			return 0;
		}
		for (int i=ti[s[head]-1]+1; i<=ti[s[head]]; i++){
			if (has1[a[i].sta]==0){
				has1[a[i].sta]=1;
				tail++;
				s[tail]=a[i].sta;
				step[a[i].sta]=step[a[i].ed]+1;
			}
		}
	}//再次广搜找出答案
	cout<<-1;
	return 0;
}

参考了联合权值那道题目之后你会发现这个有些奇怪,那就对了,这就是传说中最难以预判的剪枝,倒搜剪枝!用正着搜索的话即使用同样的方法还是过不了的,然后我们再来仔细地研究一下这个代码,就会发现这里用了两次广搜,第一次广搜看上去是将能够到达的点标记,实质上是遍历has数组将不能到达的点找出来,找到不能到达的点之后再在has1数组中将他的父亲节点和他自己标记起来表示这些点不能走。然后就可以完成这道题目啦。

made by cain-

posted @ 2017-08-09 07:35  |斗蜂|  阅读(244)  评论(0编辑  收藏  举报