第三周PTA笔记 回文数+A-B(大数减法)+高精度除法+数楼梯(大数加法)
回文数
对于一个自然数n,若将n的各位数字反向排列所得的数n1与n相等,则称n为回文数,例如2332。
若给定一个N( 2<=N<=16)进制数M(M的长度在一百位以内),如果M不是回文数,可以对其进行N进制加法,最终得到回文数。
例如对于十进制数79 STEP1 : 79 + 97 = 176 STEP2 : 176 + 671 = 847 STEP3 : 847 + 748 = 1595 STEP4 : 1595 +5951 = 7546 STEP5 : 7546 + 6457 = 14003 STEP6 : 14003 + 30041 = 44044
那么对于给定的N进制数M,请判断其能否在30步以内(包括30步)得到回文数。
输入格式:
第一行包括一个正整数 N(2<=N<=16)。
第二行包括一个正整数M(一百位以内)。
输出格式:
如果可以在n步内得到回文数,输出“STEP=n”,否则输出“NO”。
输入样例1:
10
79
输出样例1:
STEP=6
输入样例2:
8
665556
输出样例2:
NO
这题不算难,但就是有好多小细节会让我在解题过程中遇到挫折。对于这题我自己写了比较多的函数,解法也是比较暴力。
思路:首先因为数字可能会超过64位,这样的数字太大,于是我用了字符数组来储存数字,再用另一个字符数组来储存原字符数组的颠倒数。相加的话就是像小学加法一样,只不过这个是要根据不同的进制规则来进位,比如八进制,当两数相加为8就要往前进一位。而根据我编写的函数就有进制相加还有数字转换的函数。
这次的代码较多的都是一些解决问题的函数,我就不附上主函数代码了,赋上一些更重要的小函数代码。
void tran_number(char a[],char b[],int c)//将字符型数字转换为其ASCII值较好计算的值
{
int i;
for(i=0;i<c;i++)
{
if(a[i]>=48&&a[i]<58)a[i]-=48;
else if(a[i]>=65&&a[i]<91) a[i]-=55;
else if(a[i]>=97&&a[i]<123) a[i]-=87;
if(b[i]>=48&&b[i]<58)b[i]-=48;
else if(b[i]>=65&&b[i]<91) b[i]-=55;
else if(b[i]>=97&&b[i]<123) b[i]-=87;
}
}
char tran_number2(char a)//这是避免遇到0是,strlen这个函数会认为这个字符已结束
{
if(a>=0&&a<9)a+=48;
else if(a>=10&&a<17) a+=55;
return a;
}
void tran(char a[],char b[],int c,int d)
{
void tran_number(char a[],char b[],int c);
char tran_number2(char a);
int i;
tran_number(a,b,d);
for(i=0;i<d;i++)
{
if(c==11){
a[i]=a[i]+b[i];
if(a[i]==10)a[i]='A';
if(a[i]>=11&&a[i]!='A'){
a[i]=a[i]%11;
a[i+1]+=1;
if(i+1==d) a[i+1]=tran_number2(a[i+1]);
}
a[i]=tran_number2(a[i]);
}
else if(c==12){
a[i]=a[i]+b[i];
if(a[i]==10)a[i]='A';
else if(a[i]==11)a[i]='B';
if(a[i]>=12&&a[i]!='A'&&a[i]!='B'){//一开始有三个点没过就是卡在了这个if判断的地方,当我在if判断之前将值未大于12的值赋上了字符A或B,这将会导致其ASCII值大于12,从而影响判断
a[i]=a[i]%12;
a[i+1]+=1;
if(i+1==d) a[i+1]=tran_number2(a[i+1]);
}
a[i]=tran_number2(a[i]);
}
else if(c==13){
a[i]=a[i]+b[i];
if(a[i]==10)a[i]='A';
else if(a[i]==11)a[i]='B';
else if(a[i]==12)a[i]='C';
if(a[i]>=13&&a[i]!='A'&&a[i]!='B'&&a[i]!='C'){
a[i]=a[i]%13;
a[i+1]+=1;
if(i+1==d) a[i+1]=tran_number2(a[i+1]);
}
a[i]=tran_number2(a[i]);
}
else if(c==14){
a[i]=a[i]+b[i];
if(a[i]==10)a[i]='A';
else if(a[i]==11)a[i]='B';
else if(a[i]==12)a[i]='C';
else if(a[i]==13)a[i]='D';
if(a[i]>=14&&a[i]!='A'&&a[i]!='B'&&a[i]!='C'&&a[i]!='D'){
a[i]=a[i]%14;
a[i+1]+=1;
if(i+1==d) a[i+1]=tran_number2(a[i+1]);
}
a[i]=tran_number2(a[i]);
}
else if(c==15){
a[i]=a[i]+b[i];
if(a[i]==10)a[i]='A';
else if(a[i]==11)a[i]='B';
else if(a[i]==12)a[i]='C';
else if(a[i]==13)a[i]='D';
else if(a[i]==14)a[i]='E';
if(a[i]>=15&&a[i]!='A'&&a[i]!='B'&&a[i]!='C'&&a[i]!='D'&&a[i]!='E'){
a[i]=a[i]%15;
a[i+1]+=1;
if(i+1==d) a[i+1]=tran_number2(a[i+1]);
}
a[i]=tran_number2(a[i]);
}
else if(c==16){
a[i]=a[i]+b[i];
if(a[i]==10)a[i]='A';
else if(a[i]==11)a[i]='B';
else if(a[i]==12)a[i]='C';
else if(a[i]==13)a[i]='D';
else if(a[i]==14)a[i]='E';
else if(a[i]==15)a[i]='F';
if(a[i]>=16&&a[i]!='A'&&a[i]!='B'&&a[i]!='C'&&a[i]!='D'&&a[i]!='E'&&a[i]!='F'){
a[i]=a[i]%16;
a[i+1]+=1;
if(i+1==d) a[i+1]=tran_number2(a[i+1]);
}
a[i]=tran_number2(a[i]);
}
else{
a[i]=a[i]+b[i];
if(a[i]>=c){
a[i]=a[i]%c;
a[i+1]+=1;
if(i+1==d) a[i+1]=tran_number2(a[i+1]);
}
a[i]=tran_number2(a[i]);
}
}
}
int judge(char a[])//判断该字符数组是否回文的函数
{
int n,i,j,y=0;
n=strlen(a);
for(i=0,j=n-1;i<=j;i++,j--)
{
if(a[i]==a[j])y++;
}
if(n%2==0){
if(y==n/2)return 1;
else return 0;
}
else{
if(y==(n+1)/2)return 1;
else return 0;
}
}
A-B
已知两个数A和B,求A-B的运算结果。
输入格式:
输入包括两个正整数A和B 。(0<A,B≤1e10086)
输出格式:
输出A-B的运算结果。
输入样例1:
3
2
输出样例1:
1
输入样例2:
11102356985410
2356985410235698
输出样例2:
-2345883053250288
思路:由于可能输入的数字会很大,long long int可能也储存不了,于是我用了字符串来储存数字。运用数学减法规则来编写函数(向前借位的思想)。解法十分暴力,容易理解。
赋上主要的函数:
//将每个字符的ASCII值转化为10以内的数字
void tran1(char a[],int n)
{
int i;
for(i=0;i<n;i++)
{
a[i]-=48;
}
}
其实写到后面感觉这步可以省去,但是由于前面很多东西需要改(因为懒),我就保留了这个函数
//将每位字符的ASCII值转化为可以输出数字字符的值
void tran2(char a[],int n)
{
int i;
for(i=0;i<n;i++)
{
a[i]+=48;
}
}
之所以再转换成这个函数,一是为了方便输出,二是如果遇到相减值为0的数字,会被认为字符到这停止,就不会再输出后面的字符了。
void cul(char a[],int n,char b[],int m)
{
int i,j,r,c,d;
tran1(a,n);
tran1(b,m);
c=n-1;
d=m-1;
while(d>=0){
if(a[c]-b[d]<0){//判断两数相减是否小于零,若小于零则要开始向前借位
if(a[c-1]-1<0){
r=c-1;
while(a[r]-1<0){
a[r]-=1;
a[r]+=10;
r--;
}
a[r]-=1;
}
else a[c-1]-=1;
a[c]+=10;
a[c]=a[c]-b[d];//一开始因为这个地方只有...啊,忘记几分了。这个地方主要解决的问题是如果向前借一位后前面的那个数字也小于0,就要再向前借
}
else a[c]=a[c]-b[d];
c--;
d--;
}
for(i=0;i<n;i++)
{
if(a[i]!=0)break;
}
j=i;
for(i=j,r=0;r<n-j;i++,r++)
{
a[r]=a[i];//若前面的字符值为0,就要将字符数组向前移
}
a[n-j]='\0';//最后修改了这个地方才AC,主要是因为我测了一组10-9的数据,出来的答案后面总会带个方框,想必是将数组前移的过程中原本的字符的ACSII码还是1导致的
tran2(a,n-j);
}
主函数如下
#include<stdio.h>
#include<string.h>
char x[10000],y[10000];
void tran1(char a[],int n);
void tran2(char a[],int n);
void cul(char a[],int n,char b[],int m);
int main()
{
scanf("%s %s",x,y);
int x1,y1;
x1=strlen(x);
y1=strlen(y);
if(x1==y1){
if(strcmp(x,y)>0){
cul(x,x1,y,y1);
printf("%s",x);
return 0;
}//第一个数大于第二个数
else if(strcmp(x,y)==0){
printf("0");
return 0;
}
else{
cul(y,y1,x,x1);
printf("-%s",y);
return 0;
}//第一个数小于第二个数
}
else if(x1>y1){
cul(x,x1,y,y1);
printf("%s",x);
return 0;
}//第一个数大于第二个数
else{
cul(y,y1,x,x1);
printf("-%s",y);
return 0;
} //第二个数大于第一个数
}
附上一个干货大数加减乘除
高精度除法
给两个正整数 a,b,求 a/b的整数部分。
输入格式:
输入共两行,每行一个正整数,分别表示 a和b。 50%数据,a,b均小于1e18, 50%数据,a,b均小于1e500。
输出格式:
输出一个整数,表示a/b的整数部分。
输入样例1:
3
2
输出样例1:
1
输入样例
24781236498237462378425347823652387423654238752372365327862
8934457724628746
输出样例2:
2773669903874014740488146558678531750078864
主要思路其实大家都知道,就是把除法通过减法来实现,再进行一些将较小的数扩大整百倍的处理,使这种方法可以不那么暴力,主要是想要记录一些自己做题过程中遇到的问题。
1.用新开数组来储存结果,这里碰到数字为0的时候会被认为是字符数组结束了,这时要将0转换为ASCII为48的值,那就要对于这个数组有位数的判断了,这时我采取了分情况讨论。
2.测试100/1时会出现“方框00”,这就是对于新开数组的位数判断错误而造成的错误答案。
这次就只附上最核心的代码了
void tran1(char a[],int n);//转换ASCII码值:大转小
void tran2(char a[],int n);//转换ASCII码值,小转大
void sub(char a[],int n,char b[],int m);//大数相减
void chu(char a[],int n,char b[],int m);//大数相除
void chu(char a[],int n,char b[],int m)
{
int t=0,i,flag=0,n1;//flag就是为了判断新开数组的位数
n1=n;//n一直在变化,为了用到最初的n,这里新设了一个n1
while((n>m)||(n==m&&strcmp(a,b)>0)){
if(n==m){
while(strlen(a)==strlen(b)&&strcmp(a,b)>=0){
sub(a,n,b,m);
c[t]+=1;
for(t=0;c[t]==10;t++)
{
c[t]=0;
c[t+1]+=1;
}
t=0;
tran2(b,m);
}
}
else{
if(a[0]>b[0]){
if(flag==0)flag=1;
for(i=m;i<n;i++)
{
b[i]+=48;
}
sub(a,n,b,n);
c[n-m]+=1;
for(t=n-m;c[t]==10;t++)
{
c[t]=0;
c[t+1]+=1;
}
t=n-m;
tran2(b,m);
}
else{
if(flag==0&&a[0]!=b[0])flag=2;
else if(flag==0&&a[0]==b[0]) flag=3;
for(i=m;i<n-1;i++)
{
b[i]+=48;
}
sub(a,n,b,n-1);
c[n-m-1]+=1;
for(t=n-m-1;c[t]==10;t++)
{
c[t]=0;
c[t+1]+=1;
}
t=n-m-1;
tran2(b,m);
}
}n=strlen(a);
}
if(flag==0){
for(t=0;c[t]==0;)
{
t++;
}
for(;c[t]!=0;)
{
t++;
}
}
else if(flag==1||flag==3)t=n1-m+1;
else t=n1-m;
tran2(c,t);
}
数楼梯
楼梯有N阶,上楼可以一步上一阶,也可以一步上两阶。那么走到第N阶楼梯共有多少种不同的走法呢?
输入格式:
一个正整数 N(1<=N<=5000),表示楼梯阶数。
输出格式:
输出一个数,表示走到第N阶楼梯有多少种走法。
注意,数据范围很大,即使是64位也可能不够存。
输入样例1:
4
输出样例1:
5
输入样例2:
400
输出样例2:
284812298108489611757988937681460995615380088782304890986477195645969271404032323901
这是数据可能会非常大的斐波那契数列,看到一个斐波那契数列的四种实现方式(C语言),但是对我好像没有什么屁用。找到解决问题的突破口是将前几个数列出来了,找到了规律。其实这个和兔子数差不多,虽然也没什么屁用,但我就是想讲,因为现在心情很烦躁,md
os:有什么不良词汇我会酌情码出,也请你们假装没看到的亚子。
规律:N=1,f=1;N=2,f=2;N=3,f=3;N=4,f=5;N=5,f=6;
这次败在对于数组的基础不扎实,心烦意乱,心气浮躁。
但也还算有收获,不然我真的会在本篇博客不停地口吐芬芳,对,忽略这些。改变了自己大数运算的方法,其实换了一张皮还是没有改变根本,是把字符串换成了int类型的数组,一个数组元素就存一个0-9内的数,然后逆序输出。
关于本次写的加法代码如下:
for(j=0;b[j]!=-1;j++)
{
if(a[j]!=-1)b[j]+=a[j];
}
for(j=0,r=0;b[j]!=-1;j++,r++)
{
if(b[j]>9){
x=b[j];
b[j]=x%10;
if(b[j+1]==-1)b[j+1]=1;
else{
b[j+1]+=x/10;
}
}
}
赋上完整代码了:
#include<stdio.h>
int main()
{ int a[10000],b[10000],c[10000];
int N,r,j,i,x;
a[0]=1;
b[0]=2;
for(i=1;i<10000;i++)
{
a[i]=-1;
b[i]=-1;
c[i]=-1;
}
scanf("%d",&N);
if(N==0)printf("0");
else if(N==1)printf("1");
else if(N==2)printf("2");
else{
for(i=0;i<N-2;i++)
{
for(j=0;b[j]!=-1;j++)
{
c[j]=b[j];
}
for(j=0;b[j]!=-1;j++)
{
if(a[j]!=-1)b[j]+=a[j];
}
for(j=0,r=0;b[j]!=-1;j++,r++)
{
if(b[j]>9){
x=b[j];
b[j]=x%10;
if(b[j+1]==-1)b[j+1]=1;
else{
b[j+1]+=x/10;
}
}
}
for(j=0;c[j]!=-1;j++)
{
a[j]=c[j];
}
}
for(i=r-1;i>=0;i--)
{
printf("%d",b[i]);
}
}
return 0;
}
这次做题过程中学到了memset,但测试了一下怎么比for的循环赋值耗费的时间还长,差不多6ms。赋上干货C语言数组初始化的三种常用方法,填补了我知识的漏洞。
有一个很大的心得:看了别人的很多题解,其实具体做法完全没有理解,只掌握了一个大概思路,在自己能力还没那么高的前提下,我觉得还是先用自己的思维把题给解出来,这样才是自己完全地理解性地做题目,其实我心知肚明自己的方法笨得要死,但是达到目的不就行啦哈哈,更巧妙地题解可能在自己学习和能力提高的过程中再慢慢理解了。