标签:
图论当中的术语,假设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; }
版权声明:本文为博主原创文章,未经博主允许不得转载。
标签:
原文地址:http://blog.csdn.net/hy1405430407/article/details/47747917