北理乐学回文数的思考(附代码)

文章讲述了如何编写一个程序,针对给定的N进制数(2≤N≤10或N=16)M,找出最少步骤得到回文数。通过字符数组处理进制转换,以及构建加法、比较和反转函数来实现算法。如果在30步内无法得到回文数,则输出“Impossible!”。文章提供了两种解决方案,一种是通过ASCII码转换,另一种是直接读取十六进制并转换为十进制进行处理。
摘要由CSDN通过智能技术生成

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档


题目

若一个数(首位不为零)从左向右读与从右向左读都一样,我们就将其称之为回文数。

例如:给定一个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


提示:以下是本篇文章正文内容,下面案例可供参考(第一次写哈,纯纯新手,有错误随时指出,谢谢大家啦!)

目录

题目

思路分析

那么A这种怎么办?

总结


思路分析

        面对这样的一道题,乍一看确实有点眼晕。但是,只要我们理清思路,就能够化繁为简,轻松写出属于自己的答案。

        注意看,这道题有十六进制的限制,也就是说会有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进制数,得到了整个问题的解。经过分析,这道题综合性很强,每一小步都需要一些思考和总结,在做完题目的时候真的需要及时复盘,这样才能更好的进步。就写到这里了,祝学习进步!

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值