1. 程式人生 > >加權並查集

加權並查集

void print 食物鏈 lag 遍歷 並查集 read 否則 忽略

加權並查集是一種特殊的並查集,除可提供查詢操作外,還可用於表示元素與其代表元素的關系。下面以食物鏈為例,講解一下加權並查集。

#include<cstdio> //調用cstdio庫,使用getchar函數
#include<cctype> //調用cctype庫,使用isdigit函數,返回參數是否為整數
int N,K,ans,r,x,y; //定義變量用於有幾個動物,幾句話,答案,以及動物的關系和兩個要描述的動物 
int f[50005],d[50005]; //定義數組記錄代表元素,以及到達代表元素的距離 
inline int get_num() { //內聯get_num函數,讀入整數
    int num = 0; //定義整型變量num,並賦值為0
    char c; //定義字符型變量c
    bool flag = false; //定義布爾值變量flag,並置為假
    while ((c = getchar()) == ‘ ‘ || c == ‘\n‘ || c == ‘\r‘); //忽略開頭的空格,換行符及回車符
    if (c == ‘-‘) flag = true; //如果讀到負號,就將負數標誌置為真
    else num = c - ‘0‘;    //否則將讀到的第一個數字保存;
    while (isdigit(c = getchar())) //只要讀入的還是數字,就循環
        num = num * 10 + c - ‘0‘; //將num整體前移一位,並將新讀入的一位數字保存
    return (flag ? -1 : 1) * num; //返回讀到的整數,若負數標誌為真,就返回其相反數,即一個負數
}
int find(int i) { //定義函數查詢代表元素以及到達代表元素的距離 
	if(i==f[i]) return i; //找到代表元素返回 
	int oldf=f[i]; //記錄下元素之前的代表元素
	f[i]=find(f[i]); //更新元素的代表元素 
	d[i]=(d[i]+d[oldf])%3; //更新元素與代表元素的距離 
	return f[i]; //返回代表元素 
}
void check(int r,int x,int y) { //定義函數檢查是否為假話 
	if(x>N||y>N) {++ans;return;} //若動物編號超出範圍,則為假話 
	if(r==2&&x==y) {++ans;return;} //若說動物自己吃自己,則為假話 
	if(find(x)==find(y)) { //若兩個動物在同一集合中 
		if((d[x]-d[y]+3)%3!=r-1) ++ans;return; //動物之間的距離不符合描述,則為假話 
	}
	else {
		d[f[x]]=(3-d[x])%3; //求出代表元素到該元素的距離 
		f[f[x]]=x;f[x]=y; //讓代表元素指向該元素,即該元素成為代表元素,並指向y 
		d[f[y]]=(3-d[y])%3; //同上 
		f[f[y]]=y;f[y]=y;d[y]=0; // 同上,並讓y成為代表元素,令其到代表元素的距離為0 
		if(r==1) d[x]=0; //若為同類,則x到y距離為0 
		else d[x]=1; //若x吃y,則x到y距離為1 
		/* 另一種合並方法 
		f[f[y]]=f[x]; //讓y的代表元素指向x的代表元素 
		d[f[y]]=(d[x]-d[y]-(r-1)+3)%3; //更新y的代表元素到其代表元素的距離 
		*/ 
	}
}
int main() {
	N=read();K=read(); //讀入N,K 
	for(int t=1;t<=N;++t) f[t]=t; //初始化每個元素的代表元素 
	for(int t=1;t<=K;++t) { //遍歷每句話 
		r=read();x=read();y=read(); //讀入r,x,y
		check(r,x,y); //調用check函數 
	}
	printf("%d",ans); //打印答案 
	return 0;
}

加權並查集