VIJOS1683有根树的同构问题

题目描述

所谓图的同构是指两个图“相同”。图的同构有着广泛的应用,比如当要对一批图施行某种操作的时候,如果能发现其中有一些图是同构的,就可以在这些同构的图中只保留一个,从而降低工作量。例如,图1所示的T1和T3就是同构的。
图片
图的同构的定义:给出两个图G1=(V1,E1),G2=(V2,E2)。如果存在一个V1到V2的一一映射f,使得(x,y)是G1的边,当且仅当(f(x),f(y))是G2的边,则称G1和G2是同构的。也就是说,我们只关心顶点间的拓扑关系而不关心顶点的编号。
任意图的同构的判定尚无有效的算法,但要判断两棵树是否同构则要容易些。下面我们仅考虑有根树(即树形图:有向图,存在一个根,入度为0,从根到其他任一顶点恰好有一条有向路)。给出k棵有根树T1,T2,…,Tk,每棵树都有n个顶点,你的任务是求出这些树在同构关系下的所有等价类(如果两棵树同构,则它们属于同一个等价类)。
输入格式
输入的第1行包含两个整数k(1<=k<=100)和n(1<=n<=50),表示总共有k棵树,每棵都是n个顶点。接下来k行,每行描述一棵树;每行包含n-1对整数,表示这棵树的n-1条有向边;数字间用空格隔开。顶点的编号为1到n,每对整数xy表示存在一条x指向y的有向边。树的编号和在数据中出现的顺序一致,也就是说输入文件中第2行描述的是T1,第3行描述的是T2,……,第k+1行描述的是Tk。
输出格式
把给出的k棵树划分为不同的等价类,使得同一等价类中任意两棵树同构。对于每个等价类,从小到大输出这个等价类中的树的编号,用等号隔开。如果有m个等价类,则按字典序输出,每个一行。例如,有4个等价类{4,2,7},{5,1,3},{8,9},{6},则输出
1=3=5
2=4=7
6
8=9
注意,数字和等号之间不要有空格;行首和行末可以有空格。
样例输入1
3 7
7 2 7 1 7 6 2 3 1 4 6 5
7 2 7 1 2 3 1 4 1 5 5 6
4 3 3 2 4 1 1 7 5 6 4 5
样例输出1
1=3
2

分析

使用树的最小表示法就可以了。

如何将树转化为最小表示呢?
我们要将树转化为括号表示(这样方便比较)
当计算某个节点为根的树的最小表示时
首先我们递归处理得到所有子树的最小表示字符串
然后我们将这些字符串排序,依次连接成一个长字符串
接着我们在连接好的字符串左边加上个’(‘,右边加上个’)’
即可得到这棵树的最小括号表示

#include<cstdio>
#include<string>
#include<vector>
#include<algorithm>
using namespace std;
struct edge{
    int y,ne;
};
const int maxk=200,maxn=100;
int k,n,x,y,et,b[maxn+10];
bool p[maxn+10];
string s[maxk+10];
edge e[maxn+10];
void add_edge(int x,int y){
    e[++et].y=y;
    e[et].ne=b[x];
    b[x]=et;
}
string dfs(int x){
    vector<string> str;
    str.clear();
    if (!b[x])
        return "()";
    int i=b[x];
    while (i){
        str.push_back(dfs(e[i].y));
        i=e[i].ne;
    }
    sort(str.begin(),str.end());
    string ans;
    ans="(";
    int si=str.size();
    for (int i=0;i<si;i++)
        ans+=str[i];
    ans+=')';
    return ans;
}
int main(){
    scanf("%d%d",&k,&n);
    for (int i=1;i<=k;i++){
        for (int j=1;j<=n;j++){
            p[j]=0;
            b[j]=0;
        } 
        et=0;
        for (int j=1;j<n;j++){
            scanf("%d%d",&x,&y);
            add_edge(x,y);
            p[y]=1;
        }
        for (int j=1;j<=n;j++)
            if (!p[j]){
                s[i]=dfs(j);
                break;
            }       
    }
    for (int i=1;i<=k;i++)
        p[i]=0;
    for (int i=1;i<=k;i++)
        if (!p[i]){
            printf("%d",i);
            for (int j=i+1;j<=k;j++)
                if (s[i]==s[j]){
                    p[j]=1;
                    putchar('=');
                    printf("%d",j);
                }
            putchar('\n');
        }
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值