剛剛接觸並查集,還不是很熟練,先從板子題開始吧
poj 2524 Ubiquitous Religions
題目大意: 統計一羣人中加的宗教有最多有幾種。
題解: 將m組數據挨個讀入,進行合併,先尋找每個人的祖先,如果祖先不同,則進行合併。最後統計祖先是本身的個數即爲answer
AC代碼:
#include <iostream>
using namespace std;
int n,m,time=0;
int parent[50001];
int ranks[50001];//合併時如果兩棵樹的rank不同,則從rank小的向rank大的連邊
void initialize()//初始化parent數組
{
for(int i=1;i<=n;i++)
{
parent[i]=i;
ranks[i]=0;
}
}
int find_root(int x)//查找祖先
{
int x_root=x;
while(parent[x_root]!=x_root)
{
x_root=parent[x_root];
}
return x_root;
}
void unite(int x,int y)//合併
{
int x_root=find_root(x);
int y_root=find_root(y);
if(x_root!=y_root)
{
if(ranks[x_root]<ranks[y_root])
parent[x_root]=y_root;
else
if(ranks[y_root]<ranks[x_root])
parent[y_root]=x_root;
else
{
parent[y_root]=x_root;
ranks[x_root]++;
}
}
}
int main()
{
while(cin>>n>>m&&n+m)
{
int ans=0;
initialize();
for(int i=0;i<m;i++)
{
int x,y;
cin>>x>>y;
unite(x,y);
}
time++;
for(int i=1;i<=n;i++)
{
if(parent[i]==i)
ans++;
}
cout<<"Case "<<time<<": "<<ans<<endl;
}
}
poj 1611 The Suspects
題目大意: 0是病人,與0同組的其他人都是嫌疑人,並且其他與嫌疑人一個組的也是嫌疑人(一旦組中的成員是嫌疑人,該組中的所有成員都是嫌疑人 )。求共有多少嫌疑人。
題解: 此題用並查集將與0有接觸的人合併在一起,最後計算所有成員中與0同祖先的成員個數。
AC代碼:
#include <iostream>
using namespace std;
int n,m;
int parent[30001],ranks[30001];
void init()
{
for(int i=0;i<=n;i++)
{
parent[i]=i;
ranks[i]=0;
}
}
int find_root(int x)
{
while(parent[x]!=x)
{
x=parent[x];
}
return x;
}
void unite(int x,int y)
{
x=find_root(x);
y=find_root(y);
if(x!=y)
{
if(ranks[x]>ranks[y])
parent[y]=x;
else
if(ranks[x]<ranks[y])
parent[x]=y;
else
{
parent[y]=x;
ranks[x]++;
}
}
}
int main()
{
while(cin>>n>>m&&n+m)
{
init();
for(int i=0;i<m;i++)
{
int t,a,b;
cin>>t;
for(int j=0;j<t;j++)
{
if(j==0)
cin>>a;
else
{
cin>>b;
unite(a,b);
a=b;
}
}
}
int root=find_root(0);
int ans=0;
for(int i=0;i<=n;i++)
{
if(find_root(i)==root)
ans++;
}
cout<<ans<<endl;
}
}
poj 2236 Wireless Network
題目大意: 地震將電腦網絡全都打破了,需要挨個連接起來,給出電腦數量和能夠支持的最長距離。並且有兩個操作,一個操作是修復電腦,另一個是檢驗兩個電腦是否可以連接在一起。也就是說,如果計算機A和計算機B可以直接通信,或者計算機C可以與A和A進行通信,則計算機A和計算機B可以進行通信。
題解: 此題用並查集寫,先排除兩個電腦距離大於規定距離的電腦,然後再維護並查集就可以了。
AC代碼:
#include <iostream>
#include <string.h>
using namespace std;
int x[1010],y[1010];
int vis[1010],parent[1010],ranks[1010];
int n,d;
bool check(int a,int b)
{
if((x[a]-x[b])*(x[a]-x[b])+(y[a]-y[b])*(y[a]-y[b])>d*d)
return false;
return true;
}
void init()
{
for(int i=1;i<=n;i++)
{
parent[i]=i;
ranks[i]=0;
}
}
int find_root(int x)
{
while(parent[x]!=x)
x=parent[x];
return x;
}
void unite(int a,int b)
{
a=find_root(a);
b=find_root(b);
if(a!=b)
{
if(ranks[a]<ranks[b])
parent[a]=b;
else
if(ranks[a]>ranks[b])
parent[b]=a;
else
{
parent[b]=a;
ranks[a]++;
}
}
}
int main()
{
cin>>n>>d;
memset(vis,0,sizeof(vis));
init();
for(int i=1;i<=n;i++)
cin>>x[i]>>y[i];
char tmp;
while(cin>>tmp)
{
if(tmp=='O')
{
int a;
cin>>a;
vis[a]=1;
for(int i=1;i<=n;i++)
{
if(i==a||!vis[i])//如果還沒有被修復就不用合併了
continue;
if(!check(i,a))//檢查是否符合距離的最值
continue;
unite(i,a);
}
}
else
{
int a,b;
cin>>a>>b;
if(find_root(a)==find_root(b))
cout<<"SUCCESS"<<endl;
else
cout<<"FAIL"<<endl;
}
}
}
poj 1308 Is It A Tree?
題目大意: 判斷所給出的圖是否是樹。樹的條件爲:只有一個根節點,每個節點的入度不超過1。
題解: 總的來說,就是判斷一個圖中是否有環。這也正是並查集的作用之一。
AC代碼:
#include <iostream>
#include <set>
using namespace std;
typedef long long int ll;
ll parent[100001];
int flag=0;
void init()
{
for(int i=0;i<100001;i++)
parent[i]=i;
}
int find_root(int x)
{
return parent[x]==x ? x : parent[x]=find_root(parent[x]);
}
void unite(int x,int y)
{
x=find_root(x);
y=find_root(y);
if(x!=y)
parent[y]=x;
else
flag=1;//如果兩個點的根節點相同,則說明有環
}
int main()
{
ll a,b,k=0;
while(1)
{
flag=0;
k++;
init();
set<ll>st;
cin>>a>>b;
if(a==-1&&b==-1)
return 0;
if(a==0&&b==0)
{
cout<<"Case "<<k<<" is a tree."<<endl;
continue;
}
if(a==b)
flag=1;
st.insert(a);
st.insert(b);
parent[b]=a;
while(cin>>a>>b&&a+b)
{
unite(a,b);
if(a==b)//如果兩個點相等也不符合要求,自己不能指向自己
flag=1;
st.insert(a);
st.insert(b);
}
set<ll>::iterator it=st.begin();
int tmp=find_root(*it);
it++;
for(;it!=st.end();it++)
{
if(find_root(*it)!=tmp)
{
flag=1;//如果有多個根節點也不符合要求
break;
}
}
if(flag)
cout<<"Case "<<k<<" is not a tree."<<endl;
else
cout<<"Case "<<k<<" is a tree."<<endl;
}
}
hdoj 1272 小希的迷宮
題目大意: 判斷一個圖是不是樹
題解: 並查集判環。即當把每組數據合併到樹上時,兩個數的根節點不能相等,若相等則說明圖上有環,即從一個點到另一個點有兩條及以上的路徑,不符合要求。當合並數據時,發現兩個數據的根節點相等,則標記 flag=1(即不符合要求)具體做法同上題相似,只是這個題要注意,當輸入的兩個數據相等時,也是符合要求的,但上一題就不符合emm
AC代碼:
#include <iostream>
#include <set>
using namespace std;
typedef long long int ll;
int flag=0;
ll parent[100010];
void init()
{
for(int i=0;i<100010;i++)
parent[i]=i;
}
int find_root(int x)
{
return parent[x]==x ? x : parent[x]=find_root(parent[x]);
}
void unite(int x,int y)
{
x=find_root(x);
y=find_root(y);
if(x==y) //若根節點相同,則有環,不符合要求,標記flag=1
flag=1;
else
parent[y]=x;
}
int main()
{
ll a,b;
while(1)
{
flag=0;
init();
set<ll>st;
cin>>a>>b;
if(a==0&&b==0)
{
cout<<"Yes"<<endl;
continue;
}
if(a==-1)
break;
// if(a==b) 這裏不用判斷輸入的兩個數是否相等
// flag=1;
st.insert(a);
st.insert(b);
parent[b]=a; //第一對數據也別忘了合併!!!
while(cin>>a>>b&&a+b)
{
// if(a==b)
// flag=1;
unite(a,b);
st.insert(a);
st.insert(b);
}
set<ll>::iterator it=st.begin();
int tmp=find_root(*it); //這裏一定要寫find_root,不能寫成parent[]!!!
it++;
for(;it!=st.end();it++)
{
if(find_root(*it)!=tmp) //還有這裏
{
flag=1;
break;
}
}
if(flag)
cout<<"No"<<endl;
else
cout<<"Yes"<<endl;
}
}