• ZOJ3330 Country F


    Country F

    Time Limit: 2 Seconds      Memory Limit: 32768 KB

    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 <= xy < Nx != 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");
        }
    }
    

      



    也许有挫折,但这些,怎能挡住湘北前进的步伐
  • 相关阅读:
    在GDI+中如何实现以左下角为原点的笛卡尔坐标系
    html中内联元素和块元素的区别、用法以及联系
    HttpClient超时设置
    springMVC实现文件上传
    IDEA生成serialVersionUID的警告
    java中两个字符串如何比较大小
    gerrit简版教程
    mysql中OPTIMIZE TABLE的作用及使用
    mysql慢查询日志分析
    checkStype和findBugs校验
  • 原文地址:https://www.cnblogs.com/Fatedayt/p/2577871.html
Copyright © 2020-2023  润新知