1. 程式人生 > >求圖中的最小環【洛谷P2661】

求圖中的最小環【洛谷P2661】

求有向圖中的最小環問題。

對於入度為0的點,肯定不在環內。我們就把入度為0的點刪掉。注意,刪掉入度為0的點,可能造成與之相鄰點的入度為0,我們就遞迴把聯通的結點全部刪掉。

然後對於每個環,我們遍歷所有剩下的環,儲存一個最小值就OK啦!

程式碼:

#include <bits/stdc++.h>
using namespace std;
const int maxn = 2e5+7;
const int INF = 1e9+7;
int a[maxn];
int in[maxn];
bool vis[maxn];
int ans = INF;
void remove(int s)
{
	vis[s] = 1;
	in[a[s]]--;
	if(!in[a[s]] && !vis[a[s]])
	{
		remove(a[s]);
	}
} 
void dfs(int s,int e,int stp)
{
	if(s==e && stp)
	{
		ans = min(ans,stp);
		return;
	}
	if(!vis[a[s]])
	{
		vis[a[s]] = 1;
		dfs(a[s],e,stp+1);
	}
}
int main()
{
	int n;
	cin>>n;
	for(int i=1;i<=n;i++)
	{
		cin>>a[i];
		in[a[i]]++;
	}
	for(int i=1;i<=n;i++)
	{
		if(!in[i] && !vis[i])
		{
			remove(i);
		}
	}
	for(int i=1;i<=n;i++)
	{
		if(!vis[i])
		{
			dfs(i,i,0);
		}
	}
	cout<<ans<<endl;
	return 0;
}

我去看看並查集求最小環,看會了把這個補上。

UPD:並查集做法大概看了一下,下面講並查集的做法:

並查集求有向圖最小環:

如果有兩個結點的父節點相同,那麼這個圖是環。

環的長度是d[a]+d[b]+1,這個很好理解吧,畫個圖就明白了。

如果a到b有邊,那麼就把a連到b上,而且d[a] = d[b]+1,這個也很好理解吧。

然後求結點到父節點的的長度就是在路徑壓縮的時候更新一下d,d[x] += d[last]。

程式碼:

#include <bits/stdc++.h>
using namespace std;
const int maxn = 2e5+7;
const int INF = 1e9+7;
int ans = INF;
int p[maxn];
int d[maxn];
void init()
{
	for(int i=0;i<maxn;i++)
	{
		p[i] = i;
	}
}
int find(int x)
{
	if(p[x]!=x)
	{
		int last = p[x];
		p[x] = find(p[x]);
		d[x] += d[last];
	}
	return p[x];
}
void check(int a,int b)
{
	int x = find(a);
	int y = find(b);
	if(x!=y)
	{
		p[x] = y;
		d[a] = d[b]+1;
	}
	else
	{
		ans = min(ans,d[a]+d[b]+1);
	}
}
int main()
{
	int n;
	cin>>n;
	init();
	for(int i=1;i<=n;i++)
	{
		int tmp;
		cin>>tmp;
		check(i,tmp);
	}
	cout<<ans<<endl;
	return 0;
}