程序自動分析(NOI2015/BZOJ4195)洛谷P1955(並查集)

最近在學習並查集,也做了幾道題,總結一波

並查集

在計算機科學中,並查集是一種樹型的數據結構,用於處理一些不交集(Disjoint Sets)的合併及查詢問題。有一個聯合-尋找演算法(union-find algorithm)定義了兩個用於此數據結構的操作:

Find:確定元素屬於哪一個子集。它可以被用來確定兩個元素是否屬於同一子集。
Union:將兩個子集合併成同一個集合。
由於支援這兩種操作,一個不相交集也常被稱爲聯合-尋找數據結構(union-find data structure)或合併-尋找集合(merge-find set)。其他的重要方法,MakeSet,用於建立單元素集合。有了這些方法,許多經典的劃分問題可以被解決。

爲了更加精確的定義這些方法,需要定義如何表示集合。一種常用的策略是爲每個集合選定一個固定的元素,稱爲代表,以表示整個集合。接着,Find(x) 返回x 所屬集合的代表,而Union 使用兩個集合的代表作爲參數。

模板

int fa[SIZE];
//並查集的初始化
for(int i=1;i<=n;i++)
    fa[i] = i;
//並查集的Get操作,查詢一個元素屬於哪個集合
int get(int x){
     if(x == fa[x])	return x;
     return fa[x] = get(fa[x]);//路徑壓縮,fa直接賦值爲代表元素
}
//並查集的Merge操作,將兩個集合合併成一個大集合
void merge(int x,int y){
      fa[get(x)] = get(y);
}

這個模板還是很好的,可以直接套用。

程序自動分析(NOI2015/BZOJ4195)洛谷P1955

程序自動分析(NOI2015)在其他OJ沒有找到這道題,最後在洛谷裏發現了,注意下洛谷這道題的輸入輸出還是按照正常的輸入輸出,還是就是它沒有給出數據量:1<=n<=1e5,1<=x<=1e9。
在這裏插入圖片描述

思路

利用並查集進行動態維護,剛開始,所有的變量各自構成一個集合;對於每條“相等”的約束條件,合併它約束的兩個變量所在的集合即可。最後,再掃描“所有不等”類型的約束條件。如果存在一條“不等”的約束條件,它約束的兩個變量處於同一個集合內,則不可能被滿足。如果不存在這樣的“不等”約束,則全部條件都可以滿足。

代碼

#include<bits/stdc++.h>
//# define LOCAL
using namespace std;
const int SIZE = 100005;
int fa[SIZE*2];//並查集
int d[SIZE*2];//離散化數組

struct node{
	int x;
	int y;
	int e;
}a[SIZE];//存儲輸入值
int find(int x){
	if(x == fa[x])
		return x;
	return fa[x] = find(fa[x]);
}
void merge(int x,int y){
	fa[find(x)] = find(y);
}
bool cmp(node a,node b){
	return a.e > b.e;
}

int main(){
#ifdef LOCAL
	freopen("prog.in","r",stdin);
	freopen("prog.out","w",stdout);
#endif
	int t;
	cin >> t;
	while(t--){
		bool flag = 1;//作爲最後結果判斷
		int n;
		cin >> n;
		for(int i=1;i<=n;i++){
			scanf("%d%d%d",&a[i].x,&a[i].y,&a[i].e);
			d[2*i-1] = a[i].x;
			d[2*i] = a[i].y;
		}
		sort(d,d+2*n+1);//排序 
		int reu = unique(d+1,d+2*n+1) - d;//去重 
		for(int i=1;i<=n;i++){
			a[i].x = lower_bound(d+1,d+reu,a[i].x) - d;
			a[i].y = lower_bound(d+1,d+reu,a[i].y) - d;
		}//根據去重後的數組更新結構體成員的值,離散化
		for(int i=1;i<=reu;i++)
			fa[i] = i;//初始化 
		sort(a+1,a+n+1,cmp);//按e排序 
		for(int i=1;i<=n;i++){
			int r1 = find(a[i].x);
			int r2 = find(a[i].y);
			if(a[i].e)
				merge(r1,r2);//判斷條件,合併集合
			else if(r1 == r2){ 
				flag = 0;
				break;
			}
		}
		if(flag){
			cout << "YES" << endl;
		}else
			 cout << "NO" << endl;
	}
	return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章