並查集 poj 1182 食物鏈
阿新 • • 發佈:2018-12-10
首先,並查集是一種來管理元素分組情況的資料結構。時間複雜度為O(a(n))//阿克曼函式的反函式,比O(log(n))還要快。
這題先用基礎的方法來看,先介紹並查集的基本形式(模板型別):
初始化:
void Init(int n){
for(int i = 1;i <= n*3; ++i){
par[i] = i;//各點本身的父節點初始化本身
rank1[i]=0;//各個點的高度為0;有的編譯器直接定義rank會報摸稜兩可
}
}
最開始無邊。
合併:(為了防止退化)
合併時對於兩棵樹rank不同,那麼從rank小的向rank大的連;
通過路徑壓縮,使查並集更高效。對於每個節點,直接與他的根節點相連線,這種情況,為了簡單,即使樹的高度發生變化,也不修改rank的值(本題);
void Unite(int x,int y){//合併x,y類;
x = Find(x);
y = Find(y);
if(x == y) return;
if(rank1[x] < rank1[y])
par[x] = y;
else{
par[y] = x;
if(rank1[x] == rank1[y]) rank1[x]++;
}
}
查詢:
找到兩個點是否是同一組
int Find(int x){ return x ==par[x] ? x : Find(par[x]);//查詢x的祖宗; } bool Same(int x,int y){//判斷x,y是否是同一類(同一祖宗) return Find(x) == Find(y); }
分割線
本題這種方法巧妙在他對3種關係同時進行了管理,化空間為時間。
//#include<bits/stdc++.h> #include<iostream> using namespace std; const int maxn = 500010;//陣列因為是n 3倍,所以還是儘量開大點; int par[maxn];//父節點 //int rank1[maxn];//高度 void Init(int n){ //並查集初始化 for(int i = 1;i <= n*3; ++i){ par[i] = i;//各點本身的父節點初始化本身 //rank1[i]=0; } } int Find(int x){ return x ==par[x] ? x : Find(par[x]);//查詢x的祖宗; } void Unite(int x,int y){//合併x,y類; x = Find(x); y = Find(y); if(x == y) return; par[x] = y; } bool Same(int x,int y){//判斷x,y是否是同一類(同一祖宗) return Find(x) == Find(y); } int N,K; int main(){ //ios::sync_with_stdio(false); 這題好像不吃關閉輸入流 ,所以還是老老實實scanf; cin>>N>>K; int ans = 0; Init(N*3);//初始化3段,0-N代表x->A(x是A類),N~2N,代表x->B,2N~3N,代表x->C;分3段管理 for(int i = 0;i<K; ++i){ int t,x,y; //cin>>t>>x>>y; scanf("%d%d%d",&t,&x,&y);//老老實實scanf; if(x<=0 || N<x || y<=0 || N<y){//可直接判斷錯誤的條件,小於0或大於N ans++;continue; } if(t == 1){//合併類,先判斷,同類 if(Same(x,y+N) || Same(x,y+2*N)){//如果找到x,y是不同類,錯誤 ans++; } else{ Unite(x,y);//因為x,y是同類,所以x+N和y+N是同類; Unite(x+N,y+N); Unite(x+N*2,y+N*2); } } else{//x吃y if(Same(x,y) || Same(x,y+2*N)){//如果找到x和y是同類或者x被y吃 ,錯誤 ans++; } else{ Unite(x,y+N);//同理 Unite(x+N,y+2*N); Unite(x+2*N,y); } } } cout<<ans<<endl; return 0; }
這題還有向量思維模式 ,(2);
最後:
programming is the most fun you can have with your clothes on.