【題解】尋找道路

題目來源:洛谷

題目描述

在有向圖 G 中,每條邊的長度均爲 1,現給定起點和終點,請你在圖中找一條從起點到終點的路徑,該路徑滿足以下條件:

1、路徑上的所有點的出邊所指向的點都直接或間接與終點連通。
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

輸出樣例1

-1

輸入樣例2

6 6
1 2
1 3
2 6
2 5
4 5
3 4
1 5

輸出樣例2

3
說明/提示
解釋1:
在這裏插入圖片描述

如上圖所示,箭頭表示有向道路,圓點表示城市。起點1 1與終點3 3不連通,所以滿足題目描述的路徑不存在,故輸出-1−1 。

解釋2:
在這裏插入圖片描述

如上圖所示,滿足條件的路徑爲1- >3- >4- >5。注意點2 不能在答案路徑中,因爲點2連了一條邊到點6 ,而點6 不與終點5 連通。

【數據範圍】

對於30%的數據,0 <n≤10,0 <m≤20;

對於60%的數據,0 <n≤100,0 < m<m≤2000;

對於100%的數據,0<n≤10000,0<m≤200000,0<x,y,s,t≤n,x,s≠t。

思路:

參考了這篇題解
要做的就是:
1、將所有的邊反向,從終點 ed 開始 bfs,標記從終點 ed 開始可以走到的點

2、枚舉每一個點,如果這個點沒有被標記,則枚舉它的每一條出邊(反向後的),如果它指向的點被標記,則說明這個被標記的點不合法,刪除,
注意,最好有第二個數組標記,在一個數組裏刪點有後效型,如果一個點開始被標記,它通過一個序號比它小的點刪除了,那麼訪問到它的時候,就會被當成開始就沒被標記的點,會通過它把合法點刪除。
以上來自 _空_的博客

3、在合法的點上走單源點最短路

code:(有點長,但是思路清晰)

#include<bits/stdc++.h>
using namespace std;
const int N=1e4+10;
int n,m,st,ed,vis[N],vis1[N],dis[N],x[N],y[N];
vector <int> a[N],b[N];
queue <int> q;
void bfs(int s) //反向標記 
{
	q.push(s);
	while (!q.empty())
	{
		int t=q.front();
		q.pop();
		vis[t]=1;
		for (int i=0;i<b[t].size();i++)
		{
			int to=b[t][i];
			if (vis[to]==0)
			{
				q.push(to);
				vis[to]=1;
			}
		}
	}
}

void cancel()
{
	memcpy(vis1,vis,sizeof(vis));
	for (int i=1;i<=n;i++)
	  if (vis[i]==0)
	  {
          for (int j=0;j<b[i].size();j++) 
	        vis1[b[i][j]]=0;
	  }
	for (int i=1;i<=n;i++)
	{
	    if (vis1[i]==0)
		{
		    for (int j=0;j<a[i].size();j++)
			  a[i][j]=0;	
		}	
	} 
}

void init()
{
	scanf("%d%d",&n,&m);
	for (int i=1;i<=m;i++)
	{
		int x,y;
		scanf("%d%d",&x,&y);
		if (x==y) continue;
		a[x].push_back(y);//正向邊 
		b[y].push_back(x);//反向邊 
	}
	scanf("%d%d",&st,&ed);
}

void spfa(int s)
{
	while (!q.empty()) q.pop();
	for (int i=1;i<=n;i++) dis[i]=99999999,vis[i]=0;
	dis[s]=0;
	q.push(s);
	vis[s]=1;
	while (!q.empty())
	{
		int t=q.front();
		q.pop();
		vis[t]=1;
		for (int i=0;i<a[t].size();i++)
		{
			int to=a[t][i];
			if (dis[to]>dis[t]+1)
			{
				dis[to]=dis[t]+1;
				if (vis[to]==0)
				{
					q.push(to);
					vis[to]=1;
				}
			}
		}
	}
}
int main()
{
	init();
	bfs(ed); //從終點反向標記 	

    if (vis[ed]==0)  //起點無法到達終點就直接結束程序 
    {
    	cout<<-1<<endl;
    	return 0;
	}

	cancel();//撤去不符合條件的點	
	
	spfa(st);//單元點最短路 
	if (dis[ed]==99999999) cout<<-1<<endl;
	else cout<<dis[ed]<<endl;
	return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章