题目链接、
题意:
给你一个n,a,b
让你构造一个序列,长度为2^n,以a开始以b结尾,中间任意两个相邻的元素二进制相差1位
例如
2 1 3
输出
1 0 2 3
即,1开始,3结束,2^2个数01,00,10,11,相邻元素转化为二进制严格相差一位
题解:
首先,
一个n位的二进制数,与他严格相差一位的数一共有n个
例如:
与00相差一位的有10和01
与000相差一位的有100,010,001
考虑缩小问题规模,即n位与n-1位的关系
这里有一个很巧妙的想法就是n维超立方体
举例
一个2维超立方体(正方形)
00 | 01 |
10 | 11 |
3维(正方体)
先遍历n-1维超立方体然后再遍历n维超立方体(2个n-1组成1个n维)
就这样,可以写出一个dfs
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 1e5+7;
int n,a,b,sum;
void dfs(int x,int y,int ban){
if(__builtin_popcount(ban^sum) == 1){printf("%d %d ",y,x^y);return;}
for(int i=0;i<n;i++)if((~(ban>>i))&1 && (x>>i)&1)
for(int j=0;j<n;j++)if((~(ban>>j))&1 && i!=j){
dfs(1<<j,y,ban|1<<i);
dfs(x^(1<<i)^(1<<j),y^(1<<i)^(1<<j),ban|1<<i);
return;
}
}
signed main(){
scanf("%d%d%d",&n,&a,&b);
sum = (1<<n)-1;
if(__builtin_popcount(a^b)%2 == 0)return 0*puts("NO");
puts("YES");
dfs(a^b,a,0);
return 0;
}
__builtin_popcount(x)表示求x在二进制上有几个1
ban是一个标记变量,即,考虑的那些位在二进制上表现为1,那么在__builtin_popcount(ban^sum) == 1
就是说当ban和sum(111111,即n个1)相差只有1位,也就是说已经考虑完所有位数(除当前位)的时候可以判断输出了
想了好久这个东西怎么理解,最后我妥协了,模拟了下代码,大概懂了,但是,下次做到类似的题,大概还是不会、、
n = 3, a = 0 , b = 7
dfs层数 | x | y | ban | cout | i | j |
---|---|---|---|---|---|---|
1 | 111 | 000 | 000 | - | 0 | 1 |
2 | 010 | 000 | 001 | - | 1 | 2 |
3 | 100 | 000 | 011 | 000,010 | - | - |
3 | 100 | 110 | 011 | 110,010 | - | - |
2 | 100 | 011 | 001 | - | 2 | 1 |
3 | 010 | 011 | 011 | 011,001 | - | - |
3 | 010 | 101 | 011 | 101,111 | - | - |
即输出
YES
0 4 6 2 3 1 5 7
每层dfs中有两个dfs,容易看出(模拟出(瞎猜出)),这是拆分n维为两个n-1维立方体