bzoj 1081 //1081: [SCOI2005]超级格雷码 找规律+dfs/镜像/特殊进位

bzoj 1081   //1081: [SCOI2005]超级格雷码   //在线测评地址https://www.lydsy.com/JudgeOnline/problem.php?id=1081题目排版有问题
//在线测评地址https://www.luogu.org/problem/P2328题目还是看这个吧

更多题解,详见https://blog.csdn.net/mrcrack/article/details/90228694BZOJ刷题记录

该题说明,编写的程序,测试样例时,输出为

00
01
11
10

洛谷AC,bzoj WA.

编写的程序,测试样例时,输出为

00
10
11
01

洛谷AC,bzoj AC.

方法一:找规律+dfs

Accepted 820 kb 236 ms C++/Edit 695 B

//1081: [SCOI2005]超级格雷码
//在线测评地址https://www.lydsy.com/JudgeOnline/problem.php?id=1081题目排版有问题
//在线测评地址https://www.luogu.org/problem/P2328题目还是看这个吧
//题目读下来,陌生度比较大。举几个例子试试
/*
1 2
0
1

2 2
00
01
10
11

3 2
000
001
010
011//准备进位,接下来部分,要小心
101
100//此处位置有变动
110
111

4 2
0000
0001
0010
0011//准备进位,接下来部分,要小心
0101
0100//此处位置有变动
0110
0111//准备进位,接下来部分,要小心
1000
1001
1010
1011//准备进位,接下来部分,要小心
1111
1110
1100
1101
*/
//有些明白题意,但大的规律抓不住。
//此文https://blog.csdn.net/CABI_ZGX/article/details/52328700思路不错,摘抄如下
/*
这题我觉得最难的是看懂题目,其次是输出

找到规律暴力dfs就可以

是这样的
假设n=2,B=3:
答案是:
00 10 20 21 11 01 02 12 22

我们可以这样分组:

00 10 20
21 11 01
02 12 22

看出规律了吗,第一行0结尾,答案是从小到大的,第二行1结尾,答案是从大到小的,也就是说为偶数射输出从小到大,奇数从大到小,掌握这个规律,就可以写出如下代码:
*/
/*
3 3
000 100 200
210 110 010
020 120 220
221 121 021
011 111 211
201 101 001
002 102 202
212 112 012
022 122 222
*/
/*
4 3
0000 1000 2000
2100 1100 0100
0200 1200 2200
2210 1210 0210
0110 1110 2110
2010 1010 0010
0020 1020 2020
2120 1120 0120
0220 1220 2220
2221 1221 0221
0121 1121 2121
2021 1021 0021
0011 1011 2011
2111 1111 0111
0211 1211 2211
2201 1201 0201
0101 1101 2101
2001 1001 0001
0002 1002 2002
2102 1102 0102
0202 1202 2202
2212 1212 0212
0112 1112 2112
2012 1012 0012
0022 1022 2022
2122 1122 0122
0222 1222 2222
*/
//提交Wrong_Answer    820 kb    32 ms    C++/Edit    618 B.2019-11-14 21:53
/*
排查,发现
    else//奇
        for(i=B-1;i>=0;i--){
            a[step]=i;
            if(i%2==0)dfs(step+1,1);//此处错写成if(i%2==0)dfs(step+1,0);
            else dfs(step+1,0);//此处错写成else dfs(step+1,1);
        }
*/
//提交AC。2019-11-14 21:59
/*
样例如下
2 2
00
10
11
01

*/
#include
int n,B,a[20];//2^16=65536故数组开到20足够了
void dfs(int step,int w){//偶w=0,奇w=1
    int i,j;
    if(step==n+1){
        for(j=n;j>=1;j--)
            if(a[j]<=9)printf("%d",a[j]);
            else printf("%c",a[j]-10+'A');
        printf("\n");
        return;
    }
    if(w==0)//偶
        for(i=0;i             a[step]=i;
            if(i%2==0)dfs(step+1,0);
            else dfs(step+1,1);
        }
    else//奇
        for(i=B-1;i>=0;i--){
            a[step]=i;
            if(i%2==0)dfs(step+1,1);//此处错写成if(i%2==0)dfs(step+1,0);
            else dfs(step+1,0);//此处错写成else dfs(step+1,1);
        }
}
int main(){
    scanf("%d%d",&n,&B);
    dfs(1,0);
    return 0;
}

方法二:镜像

Accepted 820 kb 272 ms C++/Edit 743 B

//此文https://blog.csdn.net/xgc_woker/article/details/52328935?locationNum=1思路不错,摘抄如下
/*
每个数字要倒着输出,然后就是正常的生成Gray码即可。
生成就类似与一直给一个数+1,然后进位的时候比较特殊,要一位一位退。
还是介绍一下Gray码:
Gray码是一种数字编码方式,可以使相邻的两个数之间只有一位的差别。
以数据为例:
构造格雷码的方式很简单,首先列出
0
1
以底部为水平轴在轴下方写出轴上方的反射:
0
1
1
0

并在轴上方数字左边都加0,轴下方数字都加1:
00
01
11
10
如此类推,还可以得到:
000
001
011
010
110
111
101
100
很厉害吧,这一题,稍微改一改就好了。
*/
//为了熟悉镜像,请看下面数据的变化过程
/*
1 3
0
1
2

2 3
00
10
20
21
11
01
02
12
22

以下面这组数据为例,请看接下来的分析。

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

0 0 0
1 0 0
2 0 0
2 1 0
1 1 0
0 1 0
0 2 0
1 2 0
2 2 0
2 2 1
1 2 1
0 2 1
0 1 1
1 1 1
2 1 1
2 0 1
1 0 1
0 0 1
0 0 2
1 0 2
2 0 2
2 1 2
1 1 2
0 1 2
0 2 2
1 2 2
2 2 2

请看第1列数据,每3^1=3个数据,出现一次镜像。
请看第2列数据,每3^2=9个数据,出现一次镜像。

请看第3列数据,每3^3=27个数据,出现一次镜像。

这样就能看懂这段代码了。

for(i=0;i         for(j=n;j>=1;j--){//j列
            x=i/a[n-j+1];//根据第一列数据来写,x用来判定列上数据是增大,还是减小
            y=(i/a[n-j])%B;//y,B-1-y用来确定列上数据值
            if(x%2==0)print(y);//x%2==0,列上数字自小到打顺序0-(B-1)
            else print(B-1-y);//请注意B-1-y+y=B-1,x%2==1,列上数字自大到小逆序(B-1)-0
        }
        printf("\n");
    }

*/

//在编写过程中,因离开久了,系统待机,弄醒系统,还遇到g++崩溃的奇杷情形。
//一查,命令写错了g++ 1081.cpp 1081。正确为g++ 1081.cpp -o 1081
//样例通过,提交Wrong_Answer    816 kb    4 ms    C++/Edit    686 B .2019-11-16 17:01
//排查,奇杷错误,因文件副本过多,编译错了文件。
//修改了一些很小的错误,样例通过,提交AC。2019-11-16 17:20

#include
int a[20],sum;
int print(int c){
    if(c<=9)printf("%d",c);
    else printf("%c",c-10+'A');
}
int main(){
    int n,B,x,y,i,j;
    scanf("%d%d",&n,&B);
    a[0]=1;
    for(i=1;i<=n;i++)a[i]=a[i-1]*B;//a[0]=B^0,a[1]=B^1,a[2]=B^2,...,a[n]=B^n
    sum=a[n];//漏了此句
    for(i=0;i         for(j=n;j>=1;j--){//j列
            x=i/a[n-j+1];//根据第一列数据来写,x用来判定列上数据是增大,还是减小
            y=(i/a[n-j])%B;//y,B-1-y用来确定列上数据值
            if(x%2==0)print(y);//x%2==0,列上数字自小到打顺序0-(B-1)
            else print(B-1-y);//请注意B-1-y+y=B-1,x%2==1,列上数字自大到小逆序(B-1)-0
        }
        printf("\n");
    }
    return 0;
}

方法三:特殊进位

Accepted 820 kb 228 ms C++/Edit 488 B

//此文https://www.cnblogs.com/zyfzyf/p/4039766.html思路很棒,摘抄如下
/*
题解:
类似与一直给一个数+1,然后进位的时候比较特殊,要一位一位退。。。
*/
//样例通过,提交AC.2019-11-16 19:23
#include
int a[20],b[20],n,B;
void print(){
    int i;
    for(i=1;i<=n;i++)
        if(a[i]<=9)printf("%d",a[i]);
        else printf("%c",a[i]-10+'A');
    printf("\n");
}
int main(){
    int i,j,num=1;
    scanf("%d%d",&n,&B);
    for(i=1;i<=n;i++)num*=B,a[i]=0,b[i]=1;//数据个数
    while(num--){
        print();
        for(i=1;i<=n;i++)
            if(a[i]+b[i]>=0&&a[i]+b[i]<=B-1){
                a[i]+=b[i];
                break;
            }else b[i]*=-1;
    }
    return 0;
}

该题OJ需配的SPJ代码,即开发该题的测评系统用到

摘自https://www.luogu.org/discuss/show/36373   @Night_Aurora 2018-03-12 09:54

#include "testlib.h"

using namespace std;

int B,N;

bool Vis[100000];

int Trans(char c)
{
  int ret;
  if(c>='0'&&c<='9')
    ret=c-'0';
  else if(c>='A'&&c<='Z')
    ret=10+c-'A';
  else
    quitf(_wa,"Invalid Char %d",(int)c);
  if(ret>=B)
    quit(_wa,"Radix Out of B");
  return ret;
}

int CMP(string a,string b)
{
  if(a.size()^b.size())
    return 0x3FFFFFFF;
  if(a.size()^(N))
    quitf(_wa,"Unexpected Length");
  int ret=0;
  int wi;
  for(wi=0;wi=0;--wi)
    ret=ret*B+Trans(a[wi]);
  return ret;
}

string Inp[100000];

int main(int Argc,char**Argv)
{
  registerTestlibCmd(Argc,Argv);
  N=inf.readInt();
  B=inf.readInt();
  int CN=1;
  int wi=N;
  while(wi--)
    CN=CN*B;
  for(wi=1;wi<=CN;++wi)
    {
      if(ouf.eof())
	quitf(_wa,"Unexpected EOF in Line %d",wi);
      Inp[wi]=ouf.readLine();
      while(Inp[wi].size()&&Inp[wi][Inp[wi].size()-1]==' ')
	Inp[wi].pop_back();
    } 
for(wi=1;wi<=CN;++wi)
    {
      int Ts=Turn(Inp[wi]);
      if(Vis[Ts])
	quitf(_wa,"Repeated Value");
      Vis[Ts]=1;
      if(wi^1)
	if(CMP(Inp[wi],Inp[wi-1])^1)
	  quitf(_wa,"WA First at Pos %d",wi);
    }
  quitf(_ok,"Congulations");
  return 0;
}

 

你可能感兴趣的:(跟着大佬学算法)