【洛谷】【P5260】超立方体

传送门:超立方体


题目背景

超立方体是立方体在高维空间内的拓展(其在 2 维情况下退化为正方形,1 维情况下退化成线段)。在理论计算机科学领域里,超立方体往往可以和 2 进制编码联系到一起。对理论计算机科学颇有研究的 Will 自然也会对超立方体有着 自己的思考。

qwq

上图就是在 0~4 维空间内超立方体所对应的图形。显然我们可以把超立方体的每个顶点看成一个点,每一条棱看成一条边,这样就会得到一个无向图,我们称之为超立方图。


题目描述

D 维空间内的超立方图有 2D 个点,我们把这些点从 0 到 2D−1 依次编号。

有一个有趣而重要的充要结论是:一定存在一种编号的方式,使得图中任意两个有边相连的顶点的编号的 2 进制码中,恰好有一位不同。

在2维和3维空间内这个结论可以这样形象的理解:对于 2 维空间,我们只要把这个正方形放到第一象限内,使得 4 个顶点的坐标按逆时针顺序依次为 (0,0),(1,0),(1,1),(0,1),然后再把坐标看成 2 位 2 进制数,依次将这 4 个点编号为 0,1,3,2即可。

对于 3 维空间,同样我们可以将立方体的一个顶点与原点重合,并使得所有棱均平行于坐标轴,然后分别确定这 8 个点的坐标,最后把 3 维空间内的坐标看成一个 3 位 2 进制数即可。对于 D 维空间,以此类推。

现在对于一个 N 个点 M 条边的无向图(每个点从 0 到 N−1 编号),Will 希望知道这个图是否同构于一个超立方图。


输入格式

第一行包含一个整数 Q 表示此数据中一共包含 Q 个询问。

接下来 Q 组询问,每一组询问的输入格式如下:

第一行包含两个整数 N 和 M,接下来 M 行,每行 2 个不同的整数 x,y,表示图中存在一条无向边连接编 号为 x 和 y 的点(0≤x,y<N)


输出格式

1、如果询问中给定的图不同构于任何一个超立方图,输出 −1;

2、如果同构于某一个超立方图,那么请给图中这 N 个点重新编号,并在这一行输出 N 个用空格隔开的整数,表示原图中每个点新的编号,使得重新编号后,满足题目中所述的结论。

注意:输出文件的每一行,要么仅包含一个整数 −1,要么则应包含一个由 0 到 N−1 这 N 个数组成的排列。如果有多组解输出任意一个均可。


题解

整体结构

  1. 输入处理:首先,程序从标准输入中读取测试用例的数量。对于每个测试用例,读取节点数量 n 和边的数量 m,并建立一个无向图的邻接表。

  2. 图的结构:图被定义为一个邻接表,e 数组用于存储每个节点的相邻节点。ans 数组用于存储每个节点的计算结果,初始时为 -1,表示未被访问。

  3. 主要函数 fun()

    • 使用广度优先搜索(BFS)来遍历图,并计算 ans 数组。
    • ans[0] 初始化为 0,然后遍历 0 号节点的邻接节点,并设置对应的 ans 值。
    • 针对每个访问的节点,将其邻接节点的值合并。
    • 最后通过嵌套循环检查任意两个节点的 ans 值之间的异或结果,判断是否存在冲突情况(即这两个值的二进制表示不能同时为 1)。
  4. 条件判断:在主函数 main() 中对于每个测试用例,如果 n 不是2的幂,或边的数量 m 不等于 n/2 * log2(n),或者 fun() 返回非零值,则输出 -1。否则,输出每个节点的 ans

注意事项

  • 图条件:代码中包含了一些图的特性判断,例如判断节点数是否为2的幂,以及边的数量是否符合特定条件。
  • 位运算:使用位运算来判断节点间的关系,尤其是利用 x ^ y 来检查共同的性质,这表明 ans 值可能代表某种标记或者状态。
  • 广度优先搜索:这种搜索用于确保每个节点的状态基于其邻接节点的状态更新,从而确保了在图的遍历中,状态(或标记)能够正确地传递和合并。
    #include <iostream>
    #include <cmath>
    #include <vector>
    #include <queue>
    #include <cstring>
    using namespace std;
    vector<int>e[35000];
    int ans[35000];
    int n,m;
    int fun(){
        queue<int>q;
        ans[0]=0;
        for(int i=0;i<e[0].size();i++){
            int v=e[0][i];
            ans[v]=1<<i;
            q.push(v);
        }
        while(!q.empty()){
            int u=q.front();
            q.pop();
            for(int i=0;i<e[u].size();i++){
                int v=e[u][i];
                if(ans[v]!=-1)continue;
                q.push(v);
                ans[v]=0;
                for(int j=0;j<e[v].size();j++){
                    if(ans[e[v][j]]!=-1){
                        ans[v]|=ans[e[v][j]];
                    }
                }
            }
        }
        for(int i=0;i<n;i++){
            for(int j=0;j<e[i].size();j++){
                int tem=ans[i]^ans[e[i][j]];
                if(tem&(tem-1)){
                    return 1;
                }
            }
        }
        return 0;
    }
    int main(){
        int Q;
        cin>>Q;
        while(Q--){
            memset(ans,-1,sizeof(ans));
            cin>>n>>m;
            for(int i=0;i<n;i++)e[i].clear();
            for(int i=1;i<=m;i++){
                int x,y;
                cin>>x>>y;
                e[x].push_back(y);
                e[y].push_back(x);
            }
            if(n&(n-1)||m!=log2(n)*n/2||fun()){
                cout<<"-1"<<endl;
                continue;
            }
            for(int i=0;i<n;i++)cout<<ans[i]<<" ";
            cout<<endl;
        }
        return 0;
    }

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值