提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
题目
若一个数(首位不为零)从左向右读与从右向左读都一样,我们就将其称之为回文数。
例如:给定一个10进制数56,将56加65(即把56从右向左读),得到121是一个回文数。
又如:对于10进制数87,STEP1:87+78 = 165 STEP2:165+561 = 726 STEP3:726+627 = 1353 STEP4:1353+3531 = 4884
在这里的一步是指进行了一次N进制的加法,上例最少用了4步得到回文数4884。
写一个程序,给定一个N(2<=N<=10或N=16)进制数M(100位之内),求最少经过几步可以得到回文数。如果在30步以内(包含30步)不可能得到回文数,则输出“Impossible!”。进制N>10时,使用大写'A'字母表示10,'B'表示11,...,'E'表示15。
输入描述:两行,分别为N,M
输出描述:STEP=ans
输入例子:
9
87
输出例子:
STEP=6
提示:以下是本篇文章正文内容,下面案例可供参考(第一次写哈,纯纯新手,有错误随时指出,谢谢大家啦!)
目录
思路分析
面对这样的一道题,乍一看确实有点眼晕。但是,只要我们理清思路,就能够化繁为简,轻松写出属于自己的答案。
注意看,这道题有十六进制的限制,也就是说会有ABCDEF的字母来捣乱,我们不能像以往那样随便scanf个%d了。这里我有两种解决方案:
第一种就是通过字符数组的方式来读入,然后在进制转换的时候根据ASCII码表来进行代换。
ASCII值 | 控制字符 | ASCII值 | 控制字符 | ASCII值 | 控制字符 | ASCII值 | 控制字符 |
0 | NUL | 32 | (space) | 64 | @ | 96 | 、 |
1 | SOH | 33 | ! | 65 | A | 97 | a |
2 | STX | 34 | ” | 66 | B | 98 | b |
3 | ETX | 35 | # | 67 | C | 99 | c |
4 | EOT | 36 | $ | 68 | D | 100 | d |
5 | ENQ | 37 | % | 69 | E | 101 | e |
6 | ACK | 38 | & | 70 | F | 102 | f |
7 | BEL | 39 | ' | 71 | G | 103 | g |
8 | BS | 40 | ( | 72 | H | 104 | h |
9 | HT | 41 | ) | 73 | I | 105 | i |
10 | LF | 42 | * | 74 | J | 106 | j |
11 | VT | 43 | + | 75 | K | 107 | k |
12 | FF | 44 | , | 76 | L | 108 | l |
13 | CR | 45 | - | 77 | M | 109 | m |
14 | SO | 46 | . | 78 | N | 110 | n |
15 | SI | 47 | / | 79 | O | 111 | o |
16 | DLE | 48 | 0 | 80 | P | 112 | p |
17 | DCI | 49 | 1 | 81 | Q | 113 | q |
18 | DC2 | 50 | 2 | 82 | R | 114 | r |
19 | DC3 | 51 | 3 | 83 | X | 115 | s |
20 | DC4 | 52 | 4 | 84 | T | 116 | t |
21 | NAK | 53 | 5 | 85 | U | 117 | u |
22 | SYN | 54 | 6 | 86 | V | 118 | v |
23 | TB | 55 | 7 | 87 | W | 119 | w |
24 | CAN | 56 | 8 | 88 | X | 120 | x |
25 | EM | 57 | 9 | 89 | Y | 121 | y |
26 | SUB | 58 | : | 90 | Z | 122 | z |
27 | ESC | 59 | ; | 91 | [ | 123 | { |
28 | FS | 60 | < | 92 | \ | 124 | | |
29 | GS | 61 | = | 93 | ] | 125 | } |
30 | RS | 62 | > | 94 | ^ | 126 | ~ |
31 | US | 63 | ? | 95 | — | 127 | DEL |
我们知道: 58的时候不是什么所谓的字符'10',因为是按位存储。所以这里我们需要做个小转换。这里有个小技巧可以帮助我们快速从字符0123……9转换为数字的形式。举个例子:'4'变成4,我们可以通过间距法来快速得出:
4=‘4’-‘0’;
那么A这种怎么办?
同理哦 10=‘A’-‘0’-7;
我们看到A和9中间有7个位置,所以我们就需要减去这个7。这样我们就可以完成第一个小目标。
然后就按正常思路分析即可。这里留给大家思考,写出来的可以放在评论区一起讨论呀。
第二种解决方案是经由启发得出的,既然他十六进制限制我们,那我们直接%x读它不就完了?那有的同学问了:这也不都是十六进制呀?你这怎么弄?
别急,还没完呢。你看我慢慢道来:读完十六进制之后,我们根据题意肯定得做加法操作吧,那我们不能直接十六进制加吧,所以这里先换成十进制。代码如下:(这个转换进制相信学到现在大家能够完全掌握啦)
int N,M,a[10],b[10],step;
scanf("%d %x",&N,&M);
for(int i=0;i<10;i++){
a[i]=M%16;b[i]=0;
M/=16;
}
这里采用数组的形式,因为加法不就是一位一位加的嘛,所以数组处理起来比较方便。那么a【i】和b【i】就是两个翻转的数,根据题意设置的哈。这个10我们后期可以再调,反正我乐学过了,嘻嘻。接下来我们就需要构建几个函数。明白这个过程。
第一步,我们已经初始化了b【i】,这b里面现在都是0;现在a【i】是倒过来的,根据刚才那个程序我们就可以了解到。
这里首先设置第一个函数,我取名为reverse。他的主要目标就是把a的里面的数字反着输入进b中。但是我们不知道输入的位数是多少,所以我们首先得知道a到哪里碰见了那个‘\0’这里使用标志变量的方法:先设置flag=1,确保可以循环,从a【9】开始向前读,读到‘\0’让flag=0,这样结束循环,同时设置一个变量记录这个位数,我命名为cnt。
void reverse(int b[],int a[]){
int cnt,flag=1;
for(int i=9;i>=0&&flag;i--){
if(a[i]){
cnt=i;flag=0;
}
}
for(int i=0;i<10;i++) b[i]=0;
for(int i=0;i<=cnt;i++){
b[i]=a[cnt-i];
}
}
接下来就是反着赋值啦。很简单的
第二步,构建第二个函数,我取名为intcmp。这个是用来比较每一位的差异,如果不同就返回1;如果相同就返回0。注意:这里面依然使用了标志变量flag来帮助我们决定循环的实时状态。
int intcmp(int a[],int b[]){
int flag=0;
for(int i=0;i<10;i++){
if(a[i]!=b[i]) flag=1;
}
return flag;
}
这个函数来帮助我们决定是否继续进行加法运算。
所以第三步,我们来写加法的函数构造,我取名为add。(比较好记)
思路分析:N是那个进制,因为我们现在十六进制已经转成十进制了,但是加法还得按着进制来。
不过我们只需要明确一点:N进制就是满N进一呗,和我们所熟悉的十进制是一样的哦。所以我们先进行位数的相加。
这里有两种情况:第一种就是加完以后小于N,那么我们就不需要另行操作,直接赋给a就好。第二种就是大于N,类比十进制,我们只需要将所得数先减去N,并且在下一位上加一就可以完美实现了。以下是代码:
void add(int N,int a[],int b[]){
for(int i=0;i<10;i++){
a[i]+=b[i];
if(a[i]>=N){
a[i]-=N;a[i+1]++;
}
}
}
注:这里我将所得结果直接加到了a数组身上,这样b数组就没有发生改变。因为后续的b是a的反转,和他的自身初值没什么关系,所以就直接将所得的和赋到a身上了,在下一次的时候我们用reverse函数就行啦。
第四步,在我们构建好预备函数后,就可以来写main函数计算步数step了。(欢呼!)
int main(){
int N,M,a[10],b[10],step;
scanf("%d %x",&N,&M);
for(int i=0;i<10;i++){
a[i]=M%16;b[i]=0;
M/=16;
}
for(step=0;step<=31;step++){
reverse(b,a);
if(intcmp(a,b)){
add(N,a,b);
}else break;
}
if(step<=30) printf("STEP=%d\n",step);
else printf("Impossible!\n");
return 0;
}
这就是全貌了。分析一下:
我们通过for循环来计step的步数,一旦满足回文数条件,我们就直接跳出。如果小于等于30,输出step:如果大于,输出Impossible!循环内部:首先把a反转给b,这样有两个数组了。然后if判断这两个数组每一位是否完全一样,如果一样就满足回文数条件啦,intcmp会返回零,直接break出去。如果不一样,我们进入if内部,使用加法函数将两者相加并将新的数赋给a,step++一下,直到完全相同跳出for循环。此时step的值就可以用来判断了。
整体代码:
#include <stdio.h>
void reverse(int b[],int a[]){
int cnt,flag=1;
for(int i=9;i>=0&&flag;i--){
if(a[i]){
cnt=i;flag=0;
}
}
for(int i=0;i<10;i++) b[i]=0;
for(int i=0;i<=cnt;i++){
b[i]=a[cnt-i];
}
}
void add(int N,int a[],int b[]){
for(int i=0;i<10;i++){
a[i]+=b[i];
if(a[i]>=N){
a[i]-=N;a[i+1]++;
}
}
}
int intcmp(int a[],int b[]){
int flag=0;
for(int i=0;i<10;i++){
if(a[i]!=b[i]) flag=1;
}
return flag;
}
int main(){
int N,M,a[10],b[10],step;
scanf("%d %x",&N,&M);
for(int i=0;i<10;i++){
a[i]=M%16;b[i]=0;
M/=16;
}
for(step=0;step<=31;step++){
reverse(b,a);
if(intcmp(a,b)){
add(N,a,b);
}else break;
}
if(step<=30) printf("STEP=%d\n",step);
else printf("Impossible!\n");
return 0;
}
总结
以上就是今天要讲的内容,本文主要通过构造三个简单的函数,加法,判断回文数小条件,颠倒赋值,并且巧妙处理了N进制数,得到了整个问题的解。经过分析,这道题综合性很强,每一小步都需要一些思考和总结,在做完题目的时候真的需要及时复盘,这样才能更好的进步。就写到这里了,祝学习进步!