標籤:
圖論當中的術語,假設G=(V,E)和G1=(V1,E1)是兩個圖,如果存在一個雙射m:V→V1,使得對所有的x,y∈V均有xy∈E等價於m(x)m(y)∈E1,則稱G和G1是同構的,這樣的一個映射m稱之為一個同構,如果G=G1,則稱他為一個自同構。
簡單來說,同構圖的結點數必須相同,結構必須相同。
3.6,第一個圖形和第二個圖形的區別在於環的數量。第一個圖形為一個環,第二個為兩個環,所以不是同構圖。
若刪去z1和u1,刪去v1和w1,串連z1和w1,成為一個v1u1的鏈和z1w1x1y1的環,依舊不是同構圖,因為必須環數相同,鏈數相同。
但這還是缺少一個條件,比形A存在兩個環a1和a2,a1有3個結點,a2有5個結點,圖形B也有兩個環,b1有4個結點,b2有4個結點,依舊不是同構圖,這裡的條件就是環上或鏈上的借點數相同,和結點順序無關。
引入例題,HDU3926-Hand in Hand ,判斷兩次組成的圖形是否是同構圖。
思路之一:通過並查集確定環數/鏈數,和環內/鏈內的人數,再排序進行比較。
排序時按照人數排序,若人數相同要按照狀態排序。注意這幾點或許會比較容易過。
請先自己進行嘗試,嘗試後再參考代碼。
#include<iostream>#include<cstring>#include<cstdio>#include<math.h>#include<vector>#include<algorithm>#include<queue>#include<set>using namespace std;int pre[10100];struct e{ int a,b;};e s1[10010];e s2[10010];int find(int x){ while(x!=pre[x]) x=pre[x]; return x;}int cmp(e a,e b){ if(a.a==b.a) return a.b>b.b; else return a.a>b.a;}void init(int n){ for(int i=1;i<=n;i++) pre[i]=i;}int main(){ int t,cas=1;; scanf("%d",&t); while(t--) { for(int i=1;i<10010;i++) { s1[i].a=1;s1[i].b=0; s2[i].a=1;s2[i].b=0;//最開始每個都是獨立的,預設為鏈 } bool flag=false; int n1,m1,n2,m2; scanf("%d%d",&n1,&m1); init(n1); for(int i=0;i<m1;i++) { int a,b; scanf("%d%d",&a,&b); int dx=find(a); int dy=find(b); if(dx!=dy) { pre[dx]=dy; s1[dy].a+=s1[dx].a; s1[dx].a=0;//把拉手的孩子數量加起來,下同 } else s1[dy].b=1;//成環 } scanf("%d%d",&n2,&m2); init(n2); for(int i=0;i<m2;i++) { int a,b; scanf("%d%d",&a,&b); int dx=find(a); int dy=find(b); if(dx!=dy) { pre[dx]=dy; s2[dy].a+=s2[dx].a; s2[dx].a=0; } else s2[dy].b=1; } if(n1==n2){ sort(s1+1,s1+n1+1,cmp); sort(s2+1,s2+n2+1,cmp);//排序,若孩子的數量相同則對是否是環進行排序,這裡要注意 for(int i=0;i<n1;i++) if(s1[i].a!=s2[i].a||s1[i].b!=s2[i].b) {//判斷數量,狀態 flag=true; break; } } if(n1!=n2) flag=true; if(flag) printf("Case #%d: NO\n",cas++); else printf("Case #%d: YES\n",cas++); } return 0;}
著作權聲明:本文為博主原創文章,未經博主允許不得轉載。
圖論----同構圖