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
浙公网安备 33010602011771号