题解 P5657 【格雷码【民间数据】】
这一题
就是找规律 形式上有点像二分
考场上想复杂了以为和位运算有关
结果在错误的路上一去不复兮
回去才发现
这就是一个找规律题 ....
首先
我们观察题目给的一组处理好的串(我们称每一个经处理后的格雷数为串)
000,001,011,010,110,111,101,100,
会惊奇地发现
前四个串第一位数都为0
(000,001,011,010)
数字的加粗好像不是很明显
后四个串第一位数都是1
(110,111,101,100)
然后我们从中间分开
前四个串放一起
后四个串放一起
继续看
又会惊奇地发现
1. 在这前四个串中(即前半部分)
前两个串的第二位都是0(000,001)设这组串为a
后两个串的第二位都是1(011,010)设这组串为b
- 二分a(000,001)
前一个串的第三位都是0 (即前半部分)
后一个串的第三位都是1 (即后半部分)- 二分b(011,010)
前一个串的第三位都是1 (即前半部分)
后一个串的第三位都是0 (即后半部分)
- 二分b(011,010)
2. 在后四个串中(即后半部分)
前两个串的第二位都是1(110,111)设这组串为c
后两个串的第二位都是0(101,100)设这组串为d
- 二分c(110,111)
前一个串的第三位是0 (即前半部分)
后一个串的第三位是1 (即后半部分) - 二分d(101,100)
前一个串的第三位是1 (即前半部分)
后一个串的第三位是0 (即后半部分)
然后
我们理一下思路
就会发现一个规律
每一个阶段
前半段或后半段二分次数对应的位数是0还是1
取决于上一个阶段
也就是看k在上一个阶段
是在前半段
还是后半段
来自前半段的话
- 前半段对应位数的字符则是0
- 后半段是1
来自后半段的话
- 前半段对应位数的字符则是1
- 后半段是0
可以在处理的过程中直接输出
不需要存
还有一点!!
一定要用unsigned long long !!!!!
下面给出代码
#include <cstdio>
#include <iostream>
#include <cmath>
using namespace std;
unsigned long long k,bk;
int n;
bool flag;
int main(){
cin>>n>>k;
bk=pow(2,n-1);//从0开始编号则n-1
while(bk){ //直到长度为0
if(!flag){
if(k < bk) cout<<"0";//此处省略一个flag=false;
else if(k >= bk) {
cout<<"1";
k -= bk;//为下一阶段判断k的位置做准备
flag = true;//k在后半段下一阶段为10顺序
}
}
else if(flag){
if(k < bk){
cout<<"1";
flag = false;//若k在前半段,使下一阶段为01顺序
}
else if(k >= bk) { //如果k大于bk则在后半段
cout<<"0";
k -= bk;//为下一阶段判断k的位置做准备
//此处省略一个flag=true;
}
}
bk >>= 1;//每次去掉一半,k在前半段则去掉后半段长度,
在后半段则去掉前半段长度
}
return 0;
}
来自一位考场爆零的蒻苣