As you know, in a Tree there is exactly one path between two nodes. A Forest consists of one or more Trees. Country F has N cities, some cities are connected by roads, and these cites form a Forest with at least two trees.
The king of country F is a man with special ideas. He wants to make any two trees in the forest isomorphic. We say that two trees are isomorphic if they have same structure. That is to say, if we ignore the indices of all the nodes, the two trees are the same.
For example, in the picture shown above, the middle tree and the right tree are isomorphic, but the middle tree and the left tree are not isomorphic.
Because it is expensive to build roads, the King decides only to destroy edges. But if too many roads are destroyed, people in country F will be angry, so he can destroy at most ONE road.
Now, the King asks you whether it is possible to make any two trees isomorphic, if he destroys at most one road.
Input
There are multiple cases (no more than 60). For each case, the first line is two integers N and M (2 <= N <= 10000, 0 <= M <= N - 2), giving the number of cities and number of roads. Mlines follow, each with two integers x and y (0 <= x, y < N, x != y), means there is a road between city x and city y. You may assume that there is at most one road between any two cities and the cities forms a forest with at least two trees.
The last case is followed by two zeros.
Output
For each case, if it is possible for the King to make any two trees isomorphic when destroying at most one road, output "Yes", otherwise output "No".
Sample Input
9 7 0 1 0 2 1 3 3 4 3 5 6 7 6 8 9 7 0 1 1 2 1 3 3 4 3 5 6 7 6 8 5 2 0 1 1 2 0 0
Sample Output
Yes Yes No
------------------------------------------------------------------
题目大意:给定n个节点和m条边,构成森林,问在裁掉最多一条边的情况下,能不能使得图中的树都同构。
解题思路:这道题,思路不难,可恶的是,动手写起来,代码超长,又有点烦。首先,如果不裁掉边,那就是所有树都同构,因为是无向树,要先拓扑排序确定最里面的点,然后进行hash。再者,如果要裁掉边,那就是有一颗树的节点是其他树的节点树的两倍,因为要均分,可以一次dfs确定该裁哪条边,然后新的两棵树进行哈希。思维很清晰,动手真烦。
#include <stdio.h> #include <string.h> #include <stdlib.h> #include <algorithm> #include <vector> #include <queue> #define clr(a,b) memset(a,b,sizeof(a)) using namespace std; /*gid[i]表示i节点的树的编号,num[i]表示i的子孙的个数(方便删边 的时候找到该删除的边),pre[i]表示dfs时候的前驱节点,det[i]表示 i节点的度数(拓扑排序的时候要用),gra记录森林,que是拓扑排序时的 队列,h数组用来进行树的哈希,pa和pb用于删边*/ const int N=10005,q=15237; int n,m,gid[N],num[N],id,pre[N],det[N]; vector<int>gra[N]; int que[N],st,ed,h[N],pa,pb; /*Tree结构用来记录树,tree.num记录树的节点个数,tree.one和tree.two 记录树的两个中心点,h1和h2记录树的两次哈希*/ struct Tree{ int one,two,h1,h2,num; bool operator<(const Tree& a)const{ if(num!=a.num)return num<a.num; if(h1!=a.h1)return h1<a.h1; return h2<a.h2; } bool operator==(const Tree& a)const{ return num==a.num&&h1==a.h1&&h2==a.h2; } }tree[N]; /*进行树的遍历*/ void dfs(int s,int p){ pre[s]=p;gid[s]=id;tree[id].num++;num[s]=1; int len=gra[s].size();det[s]=len; if(s==pa||s==pb)det[s]--; if(det[s]==1||det[s]==0)que[ed++]=s; for(int i=0;i<len;i++){ int e=gra[s][i];if(e==p)continue; dfs(e,s);num[s]+=num[e]; } } /*拓扑排序,确定树最里面的点*/ void topsort(){ int u=tree[id].num; while(st<ed){ if(u==2&&ed-st==2){ tree[id].one=que[st++];tree[id].two=que[st++];return ; } if(u==1){ tree[id].one=que[st++];tree[id].two=-1;return ; } int k=ed; while(st<k){ int g=que[st++],len=gra[g].size();u--;det[g]=-1; for(int i=0;i<len;i++){ int t=gra[g][i]; if(g==pa&&t==pb)continue; if(g==pb&&t==pa)continue; det[t]--; if(det[t]==1)que[ed++]=t; } } } } /*对树进行哈希*/ int hdfs(int s,int f){ vector<int>e;e.clear(); int len=gra[s].size(),ret=1; for(int i=0;i<len;i++){ int ee=gra[s][i]; if(s==pa&&ee==pb)continue; if(ee==pa&&s==pb)continue; if(gra[s][i]!=f)e.push_back(hdfs(gra[s][i],s)); } if(e.size()==0)return 1; sort(e.begin(),e.end());len=e.size(); for(int i=0;i<len;i++)ret=(ret^e[i])*h[i]%q; return ret; } int main(){ srand(16512); for(int i=0;i<10000;i++)h[i]=rand()%q; // freopen("/home/axorb/in","r",stdin); while(scanf("%d%d",&n,&m),n+m){ for(int i=0;i<n;i++)gra[i].clear(); for(int i=0;i<m;i++){ int a,b;scanf("%d%d",&a,&b); gra[a].push_back(b);gra[b].push_back(a); } clr(gid,-1);clr(num,0);id=0;pa=pb=-1; for(int i=0;i<n;i++)//建立每棵树,以及对树哈希 if(gid[i]==-1){ st=ed=0;tree[id].num=0;dfs(i,-1); topsort();tree[id].h1=hdfs(tree[id].one,-1); if(~tree[id].two)tree[id].h2=hdfs(tree[id].two,-1); else tree[id].h2=-1; if(tree[id].h1>tree[id].h2)swap(tree[id].h1,tree[id].h2); id++; } sort(tree,tree+id);//按照树的节点个数、h1、h2的顺序对森林进行排序 int flag=0; for(int i=1;i<id;i++)flag+=!(tree[i-1]==tree[i]); if(!flag){puts("Yes");continue;}//如果原来就同构 flag=0; for(int i=1;i<id-1;i++)flag+=!(tree[i-1]==tree[i]); if(flag){puts("No");continue;}//如果除了节点最多的树以外,其他树之间不同构 if(tree[id-1].num!=tree[0].num*2){puts("No");continue;} pa=-1; for(int i=0;i<n;i++)if(num[i]==tree[0].num&&~pre[i]){pa=i;break;} if(pa==-1){puts("No");continue;}//如果找不到将树均分的点 id--;pb=pre[pa];//将树分成两部分,这里是一部分 st=ed=0;tree[id].num=0;dfs(pa,pb); topsort();tree[id].h1=hdfs(tree[id].one,-1); if(~tree[id].two)tree[id].h2=hdfs(tree[id].two,-1); else tree[id].h2=-1; if(tree[id].h1>tree[id].h2)swap(tree[id].h1,tree[id].h2); id++;swap(pa,pb);//下面是树的另一部分 st=ed=0;tree[id].num=0;dfs(pa,pb); topsort();tree[id].h1=hdfs(tree[id].one,-1); if(~tree[id].two)tree[id].h2=hdfs(tree[id].two,-1); else tree[id].h2=-1; if(tree[id].h1>tree[id].h2)swap(tree[id].h1,tree[id].h2); id++; if(tree[id-2]==tree[0]&&tree[id-1]==tree[0])puts("Yes"); else puts("No"); } }