2021.12.15 P2328 [SCOI2005]超级格雷码(找规律填空)

https://www.luogu.com.cn/problem/P2328

题意:

输出n位B进制的格雷码。

分析:

好吧,咱先写出来一部分格雷码试试。

\(n=2,B=2\) 时(这个表咱竖着看)

00 10
01 11

似乎没有什么规律,咱继续,当\(n=3,B=3\) 时(这个表咱竖着看)

000 122 200
001 121 201
002 120 202
012 110 212
011 111 211
010 112 210
020 102 220
021 101 221
022 100 222

似乎发现点规律:

1.去掉重复的数字,总是一个固定的数字循环在不断重复:

\[0,1,2,\cdots,B-1,B-1,\cdots,1,0 \]

2.从右往左数第 \(i\) 列数字在同一循环节里出现的次数为 \(B^{i-1}\)

3.这可以用除法以及取模哟~

咱继续实验,当 \(n=2,B=3\) 时:

00 01 02 12 11 10 20 21 22

这一行咱横着看,是不是依旧符合那个规律?

对于第 \(i\) 个出现的格雷码,对于它的第 \(j\) 位上数字编号(不是字母,咱多个函数转一下字符就行):

1.计算它是属于第几个出现的字符,这里咱先除去每个循环节重复的字符数,找到它是第几个不重复的字符

\[x=i\%(B^{j-1})?i/(B^{j-1})+1:i/(B^{j-1}) \]

2.计算它在每个循环节中的位置,每个循环节长度为 \(2*B\)

\[x\%=2*B \]

代码如下:

#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<bitset>
#define IOS ios_base::sync_with_stdio(false);cin.tie(0);cout.tie(0);
using namespace std;

int n,B,num[100],len[100];

inline int ksm(int x,int y){
	int fin=1;
	while(y){
		if(y&1)fin*=x;
		x*=x;
		y>>=1;
	}
	return fin;
}
inline char id(int x){
	if(x>=0&&x<=9)return (char)(x+'0');
	else return (char)(x-10+'A');
}

int main(){
	IOS;
	cin>>n>>B;
	for(int i=0;i<B;i++)num[i+1]=num[2*B-i]=i;
	//for(int i=1;i<=2*B;i++)cout<<num[i]<<" ";cout<<endl;
	int m=ksm(B,n);
	//for(int i=0;i<=35;i++)cout<<id(i)<<" ";cout<<endl;
	for(int i=n-1;i>=0;i--)len[i+1]=ksm(B,i);
	//for(int i=1;i<=n;i++)cout<<len[i]<<" ";cout<<endl;
	for(int i=1;i<=m;i++){
		for(int j=n;j>=1;j--){
			int x=i%len[j]?i/len[j]+1:i/len[j];
			x%=2*B;
			cout<<id(num[x]);
		}
		cout<<endl;
	}
	return 0;
}
 posted on 2021-12-15 20:40  eleveni  阅读(154)  评论(0)    收藏  举报