1. 程式人生 > >尋找道路——呆滯大佬der最騷暴力

尋找道路——呆滯大佬der最騷暴力

可能 再次 接下來 borde 滿足 files spf file bellman

尋找道路

題目描述:

有點的出邊所指向的點都直接或間接與終點連通。

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:

技術分享

如上圖所示,箭頭表示有向道路,圓點表示城市。起點1 與終點3 不連通,所以滿足題

目?述的路徑不存在,故輸出- 1 。

解釋2:

技術分享

如上圖所示,滿足條件的路徑為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數組中將他的父親節點和他自己標記起來表示這些點不能走。然後就可以完成這道題目啦。

尋找道路——呆滯大佬der最騷暴力