- 题库 :洛谷
- 题号 :5657
- 题目 :格雷码
- link :https://www.luogu.com.cn/problem/P5657
思路 :此题是由一个序列通过n次转换得来的,因此我们可以把这个序列还原回去,在还原的过程中得到问题的解。
做法 :设第一个格雷码的编号是0,第2个是1……题目中所求的是编号为m(题目中的k)的n位格雷码,通过观察发现这个格雷码序列的前半段的首位都是0,而后半段都是1。所以我们可以通过判断当前m是在前半段还是在后半段来判断出n位编号为m的格雷码的首位是什么,同时我们也可以找出这个格雷码是属于哪个区间了。确定当前区间后,我们可以继续通过以下方法继续确定他所在的区间以及当前这个n位格雷码的编号为m的第i个数是多少,直到把这整个区间缩成一个数为止(用二分来实现)。但是我们需要注意,当确定当前区间后想要找下一位时,其中的顺序不一定是首位为0的在前,首位为1的在后(将上一次确定的区间里所有数的首位都去掉后),但我们依然可以发现一个规律,就是当当前的m在前半段时,下一位所确定的区间一定是0在前,1在后;反之则一定是1在前,0在后。注意如果此时的m在后半段,那么m要减去区间的一半。
观察样例 :
2 3
我们得到2位格雷码有四个 :
00 01 11 10
然后我们找编号为3的格雷码在后半段(编号从0开始的),因此确定第一位为1,输出1,将m - 序列的长度 / 2(因为数据太大,所以代码中是将长度一开始设定为2的n - 1次方了,然后减的直接就是序列的长度,但是意义上和 - 长度 / 2相等),此时的m为1;因为刚刚m在后半段,所以顺序为先1再0,因为此时的m在后半段,所以输出0
代码 :
1 #include <bits/stdc++.h> 2 #define INF 0x3f3f3f3f 3 #define int unsigned long long//unsigned long long了解一下 4 using namespace std; 5 int n, m, l, f; 6 signed main() 7 { 8 scanf("%llu %llu", &n, &m); 9 l = pow(2, n - 1);//长度(数据原因,直接先/2了,所以只是2的n - 1次方) 10 for(register int i = 0; l;)//i = 0代表0在前,1在后; 反之则为1。初始时是0在前1在后 11 { 12 f = (m < l ? 0 : 1);//判断m在前半段还是后半段,前半段为0,反之为1(此时的l已经除过2了) 13 if(f)//后半段 14 { 15 printf(i == 0 ? "1" : "0");//输出 16 i = 1;//改变 17 m -= l;//别忘了减 18 } 19 else//前半段 20 { 21 printf(i == 0 ? "0" : "1");//输出 22 i = 0;//改变 23 } 24 l >>= 1;//二分 25 } 26 return 0; 27 }