並查集的使用

better_space發表於2018-04-24

並查集實際上是資料結構中樹的應用,每個子樹最終連線到一個根節點上


演算法實現,包括find()函式--找到子樹的根節點,和join()函式--合併子樹

find()演算法實現:

int find(int x) 
{
	int r=x;
	while(r!=pre[r]) 
	{
		r = pre[r];
	}
	int i = x,j = r;
	while(i != r) //路徑壓縮演算法
	{
		j = pre[i];//改變前導節點前記錄值
		pre[i] = r;//前導節點改為根節點
		i = j;
	}
	return r;
}

join()演算法實現:
void join(int x,int y) 
{
	int fx = find(x);
	int fy = find(y);
	if(fx!=fy) 
	{
		pre[fx] = fy;
	}
}


題目引入:

第八屆藍橋杯:風險度量

X星系的的防衛體系包含 n 個空間站。這 n 個空間站間有 m 條通訊鏈路,構成通訊網。 
兩個空間站間可能直接通訊,也可能通過其它空間站中轉。

對於兩個站點x和y (x != y), 如果能找到一個站點z,使得: 
當z被破壞後,x和y無法通訊,則稱z為關於x,y的關鍵站點。

顯然,對於給定的兩個站點,關於它們的關鍵點的個數越多,通訊風險越大。

你的任務是:已知網路結構,求兩站點之間的通訊風險度,即:它們之間的關鍵點的個數。

輸入資料第一行包含2個整數n(2 <= n <= 1000), m(0 <= m <= 2000),分別代表站點數,鏈路數。 
空間站的編號從1到n。通訊鏈路用其兩端的站點編號表示。 
接下來m行,每行兩個整數 u,v (1 <= u, v <= n; u != v)代表一條鏈路。 
最後1行,兩個數u,v,代表被詢問通訊風險度的兩個站點。

輸出:一個整數,如果詢問的兩點不連通則輸出-1.

例如: 
使用者輸入: 
7 6 
1 3 
2 3 
3 4 
3 5 
4 5 
5 6 
1 6 
應該輸出: 

2


#include<cstdio>
#include<cstdlib>
#include<iostream>
#include<algorithm>
using namespace std;
int pre[1005],route[1005][2];
int sum = 0;

int find(int x) 
{
	int r=x;
	while(r!=pre[r]) 
	{
		r = pre[r];
	}
	int i = x,j = r;
	while(i != r) //路徑壓縮演算法
	{
		j = pre[i];//改變前導節點前儲存值
		pre[i] = r;
		i = j;//改變前導節點的值
	}
	return r;
}

void join(int x,int y) 
{
	int fx = find(x);
	int fy = find(y);
	if(fx!=fy) 
	{
		pre[fx] = fy;
	}
}

int main() {
	int n,m;
	scanf("%d %d",&n,&m);
	for(int i = 0; i<m; i++) 
	{
		scanf("%d %d",&route[i][0],&route[i][1]);
	}

	int p1,p2;
	scanf("%d %d",&p1,&p2);
	
	
	for(int i = 0; i<n; i++) pre[i] = i;
	for(int i = 0; i<m; i++) 
	{
		join(route[i][0],route[i][1]);
	}
	int a = find(p1);
	int b = find(p2);
	if(a!=b) 
	{
		printf("-1\n");
	} 
	else 
	{
		for(int i = 1; i<=n; i++) 
		{
			if(p1 == i||p2 == i) 
			{
				continue;
			}
			for (int j = 1; j <= n; j++)pre[j] = j;
			for(int j = 0; j < m; j++) 
			{
				if(route[j][0] == i||route[j][1] == i) 
				{
					continue;
				}
				int a = find(route[j][0]);
				int b = find(route[j][1]);
				if(a>b) {
					a^=b;
					b^=a;
					a^=b;
				} 
				if(a!=b) 
				{
					pre[b] = a;
				}
			}
			a = find(p1);
			b = find(p2);
			if(a!=b) sum++;
		}
		printf("%d\n",sum);
	}
	return 0;
}

1、判定是否兩個通訊節點是否聯通

2、連通的前提下遍歷所有節點,去掉該節點與u、v連線的邊,如果u、v依然連通,說明該節點不是關鍵點,否則是關鍵點sum++

相關文章