最近在學習並查集,也做了幾道題,總結一波
並查集
在計算機科學中,並查集是一種樹型的數據結構,用於處理一些不交集(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;
}