wikioi-天梯-进入省队-并查集-1073:家族
題目描述 Description
若某個家族人員過于龐大,要判斷兩個是否是親戚,確實還很不容易,現在給出某個親戚關系圖,求任意給出的兩個人是否具有親戚關系。 規定:x和y是親戚,y和z是親戚,那么x和z也是親戚。如果x,y是親戚,那么x的親戚都是y的親戚,y的親戚也都是x的親戚。
輸入描述 Input Description
第一行:三個整數n,m,p,(n<=5000,m<=5000,p<=5000),分別表示有n個人,m個親戚關系,詢問p對親戚關系。 以下m行:每行兩個數Mi,Mj,1<=Mi,Mj<=N,表示Ai和Bi具有親戚關系。 接下來p行:每行兩個數Pi,Pj,詢問Pi和Pj是否具有親戚關系。
輸出描述 Output Description
P行,每行一個’Yes’或’No’。表示第i個詢問的答案為“具有”或“不具有”親戚關系。
樣例輸入 Sample Input
6 5 3
1 2
1 5
3 4
5 2
1 3
1 4
2 3
5 6
樣例輸出 Sample Output
Yes
Yes
No
數據范圍及提示 Data Size & Hint
n<=5000,m<=5000,p<=5000
類型:圖論? 難度:1.5題意:給出n個點的m個等價類關系(給出a b表示ab屬于同一個集合,具有傳遞性和可逆性),給出p個詢問,每個查詢輸出兩個數是否在一個集合中。
分析:典型的并查集問題,即初始時,將n個數看成n個集合,每次給出a,b時,合并ab所在的兩個集合。
用樹存儲集合,fa[i]表示點i的父節點,初始化fa[i]=i,每次合并i,j所在的集合時,分別找到i,j所在樹的根節點,如x,y,然后 fa[x]=y。
查詢i,j所在集合時,找到i,j所在樹的根節點,比較是否相同。
關于上述算法,有兩個經典優化方法:
1、每次查詢時,將查詢點到根節點路徑上的所有點的父節點置為根節點,降低樹的高度,方便下次查詢。
2、合并集合時,將高度低的樹合并到高的樹中,如height(a)>height(b),那么father[b] = a。
注意:看了大部分人的代碼,沒有做2這個優化。我估計原因是由于在合并操作中,會調用查詢操作(先要查詢需合并兩個點的根節點才能合并,我也在這犯了一個錯誤),而查詢操作已經做了1的優化,第2個優化就有點多余了(做了1的優化,樹的高度也不好記錄了),所以在實際實現中,第2個優化用的較少。
代碼:
#include<iostream> #include<cstdio> using namespace std;int n,m,p; int fa[5010];int mfind(int a) {if(fa[a] != a) fa[a] = mfind(fa[a]);return fa[a]; }void mmerge(int a,int b) {fa[mfind(a)] = mfind(b); }int main() {scanf("%d%d%d",&n,&m,&p);for(int i=1; i<=n; i++)fa[i] = i;int a,b;while(m--){scanf("%d%d",&a,&b);mmerge(a,b);}while(p--){scanf("%d%d",&a,&b);if(mfind(a) == mfind(b))printf("Yes\n");else printf("No\n");} }
?
總結
以上是生活随笔為你收集整理的wikioi-天梯-进入省队-并查集-1073:家族的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: .net mapi_使用C#.NET通过
- 下一篇: mapi java_[Security: