BitRing & 欧拉回路
题目
一个环上有 2n 个位置,把他们填上0或1,使得每连续n位都能组成二进制下不互相重复的数,求出一个序列即可。
思路
譬如n=3的时候环上是8个位置
1
2
3
4
5
6
7
8
900101110
001 = 1
010 = 2
101 = 5
011 = 3
111 = 7
110 = 6
100 = 4
000 = 0
现在需要把 0 ~ 2n-1 这些数字串起来
考察每个比特串的 前n-1位 和 后n-1位
以n=3为例
所有可能出现的组合
组合 | 接续关系 | 接续关系(Dec) |
---|---|---|
000 | 00 –> 00 | 0 –> 0 |
001 | 00 –> 01 | 0 –> 1 |
010 | 01 –> 10 | 1 –> 2 |
011 | 01 –> 11 | 1 –> 3 |
100 | 10 –> 00 | 2 –> 0 |
101 | 10 –> 01 | 2 –> 1 |
110 | 11 –> 10 | 3 –> 2 |
111 | 11 –> 11 | 3 –> 3 |
考虑右侧的组合关系,这是个有向图,每一条边代表连续n位,也就是在上面那个表里的一个箭头关系
(2指向0就是10拼上00,得100=4)
经整理,可得该图的 邻接表 :
0 –> 0,1
1 –> 2,3
2 –> 0,1
3 –> 2,3

你需要做的是找出欧拉回路,即把它一笔画下来
那么矛盾就集中在找欧拉回路上了
Question 1:
根据这个表可否推广n=4,5,6…时的邻接表?
(当然可以)
Question 2:
这个图是否一定存在欧拉回路?
(必须的)
根据:每个节点都有两个入度两个出度,而且是联通图。
欧拉回路
欧拉回路怎么求
1、从任意一个点起,在图中寻找任意一个(不需要遍历全图的)回路,最终必定以回到这个点作为结束。
2、从图中拿掉一个回路,剩下的必定是若干个回路,且和已有回路存在公共点
3、前一条性质其实是在做减法。所以我们可以反过来用加法的形式,从一个简单回路开始不断在公共点处添加回路以拼接,直到最终生成欧拉回路。
算法
求欧拉回路的算法:
1、初始回路[0,0],并手动修改生成了此回路后的邻接表
2、cnt=2n-2
循环(当cnt不为0):
cnt,L,index=AddSubRing(cnt,L,index)
伪代码
1 | AddSubRing(cnt,L,index): |
存储结构
邻接表:
\((x \mod{(M>>1)})\)
\((x\mod {(M>>1)})+1\)
欧拉回路:
链表(C语言)
List(Python)
代码
1 | n=int(input()) |
哈密顿回路
上面的方案中采用了欧拉回路,以每条边表示每一个n位二进制数。遍历所有的边求出欧拉回路即可
也可以用点来表示每一个n位二进制数,以边来表示接续关系,这种情况下就需要遍历所有的点求出一个哈密顿回路。

比对n=3时对应的欧拉图和哈密顿图,可以看到欧拉图的规模比较小
求解欧拉回路,上面已经实现了o(n)级别的算法。
而哈密顿回路是NP完全问题,并没有快捷的算法。
参考链接
这里有哈密顿图和欧拉图的dfs求解方法
哈密顿回路:
将每个点看成一个k位的二进制,每条边看成一个k+1位的二进制,那么一个点u向另一个点v连边当且仅当u去掉第一位后在后面加上一位能得到v,例如:001001向010010连边,边的二进制为00100010。可以发现,这个图一定存在一条哈密顿回路,那么第一问的答案显然是2k。对于第二问,因为k较小,直接暴力找哈密顿回路即可。
1 |
|
欧拉回路:
如果将点看成一个k−1位的二进制,边看成一个k位二进制,那么就是求一个欧拉回路,同样暴力dfs即可。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
using namespace std;
int vis[3000];
int ans[3000];
int k,n;
int mask;
bool dfs(int x,int dep){
if(dep==n)
{ return 1; }
if(!vis[x<<1]){
vis[x<<1]=1;
ans[dep+1]=x<<1;
if(dfs((x<<1)&mask,dep+1))
{ return 1; }
vis[x<<1]=0;
}
if(!vis[x<<1|1]){
vis[x<<1|1]=1;
ans[dep+1]=x<<1|1;
if(dfs((x<<1|1)&mask,dep+1))
{ return 1; }
vis[x<<1|1]=0;
}
return 0;
}
int main(){
scanf("%d",&k);
n=1<<k,mask=(1<<(k-1))-1;
printf("%d ",n);
dfs(0,0);
for(int i=1;i<=n;i++)
{ printf("%d",(ans[i]>>(k-1))&1); }
}