传送门:超立方体
题目背景
超立方体是立方体在高维空间内的拓展(其在 2 维情况下退化为正方形,1 维情况下退化成线段)。在理论计算机科学领域里,超立方体往往可以和 2 进制编码联系到一起。对理论计算机科学颇有研究的 Will 自然也会对超立方体有着 自己的思考。
上图就是在 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 个数组成的排列。如果有多组解输出任意一个均可。
题解
整体结构
-
输入处理:首先,程序从标准输入中读取测试用例的数量。对于每个测试用例,读取节点数量
n
和边的数量m
,并建立一个无向图的邻接表。 -
图的结构:图被定义为一个邻接表,
e
数组用于存储每个节点的相邻节点。ans
数组用于存储每个节点的计算结果,初始时为-1
,表示未被访问。 -
主要函数
fun()
:- 使用广度优先搜索(BFS)来遍历图,并计算
ans
数组。 ans[0]
初始化为0
,然后遍历0
号节点的邻接节点,并设置对应的ans
值。- 针对每个访问的节点,将其邻接节点的值合并。
- 最后通过嵌套循环检查任意两个节点的
ans
值之间的异或结果,判断是否存在冲突情况(即这两个值的二进制表示不能同时为1
)。
- 使用广度优先搜索(BFS)来遍历图,并计算
-
条件判断:在主函数
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; }