Codeup Contest ID:100000593
题目描述
实现一个加法器,使其能够输出a+b的值。
输入
输入包括两个数a和b,其中a和b的位数不超过1000位。
输出
可能有多组测试数据,对于每组数据,
输出a+b的值。
样例输入
6 8
2000000000 30000000000000000000
样例输出
14
30000000002000000000
思路
这题就是很典型的一个大整数的加法运算,和书上一样,定义大整数类型的结构体bign,定义一个字符串转bign类型的函数,再定义一个add函数,然后从低位开始计算。
因为这里最大要一千位,为了保险起见,建议把所有数组都开成1001,以免出现各种不必要的麻烦。
代码
#include
#include
#include
#include
#include
#include
using namespace std;
struct bign{
int d[1001];
int len;
bign(){//构造函数
memset(d, 0, sizeof(d));
len = 0;
}
};
bign change(char str[]){
bign a;
a.len = strlen(str);
for(int i=a.len-1, j=0;i>=0, j<a.len;i--, j++) a.d[j] = str[i]-'0';
return a;
}
bign add(bign a, bign b){
bign c;
int carry = 0;
for(int i=0;i<a.len||i<b.len;i++){
int temp = a.d[i]+b.d[i]+carry;
c.d[c.len++] = temp%10;
carry = temp/10;
}
if(carry!=0) c.d[c.len++] = carry;
return c;
}
char str1[1001], str2[1001];//这里要多1位,最后要存'\0'
int main(){
while(scanf("%s %s", str1, str2) != EOF){
bign a = change(str1);
bign b = change(str2);
bign c = add(a, b);
for(int i=c.len-1;i>=0;i--) printf("%d", c.d[i]);
printf("\n");
memset(str1, 0, sizeof(str1));
memset(str2, 0, sizeof(str2));
}
return 0;
}
题目描述
输入一个正整数N,输出N的阶乘。
输入
正整数N(0<=N<=1000)
输出
输入可能包括多组数据,对于每一组输入数据,输出N的阶乘
样例输入
0
4
7
样例输出
1
24
5040
思路
首先这题肯定也是大整数类的题目,因为1000的阶乘计算器算出来是带10的几次方的,而题目的要求显然是全部输出。我的思路是先把大整数的乘法函数写出来,然后再用循环函数计算阶乘即可。
但是要注意,1000的阶乘很大很大,用win10的计算器算出来约等于4×102567,因此,大整数的数组d开1000位是不够的,至少2568位。
代码
#include
#include
#include
#include
#include
#include
using namespace std;
struct bign{
int d[2568];
int len;
bign(){
memset(d, 0, sizeof(d));
len = 0;
}
};
bign multi(bign a, int b){
bign c;
int carry = 0;
for(int i=0;i<a.len;i++){
int temp = a.d[i]*b+carry;
c.d[c.len++] = temp%10;
carry = temp/10;
}
while(carry!=0){
c.d[c.len++] = carry%10;
carry /= 10;
}
return c;
}
bign f(int n){
bign a;
a.len = 1;
a.d[0] = 1;
if(n==0) return a;
else{
for(int i=1;i<=n;i++) a = multi(a, i);
return a;
}
}
int main(){
int n;
while(scanf("%d", &n) != EOF){
bign x = f(n);
for(int i=x.len-1;i>=0;i--) printf("%d", x.d[i]);
printf("\n");
}
return 0;
}
题目描述
求2个浮点数相加的和
题目中输入输出中出现浮点数都有如下的形式:
P1P2…Pi.Q1Q2…Qj
对于整数部分,P1P2…Pi是一个非负整数
对于小数部分,Qj不等于0
输入
对于每组案例,第1行是测试数据的组数n,每组测试数据占2行,分别是两个加数。
每组测试数据之间有一个空行,每行数据不超过100个字符
输出
每组案例是n行,每组测试数据有一行输出是相应的和。
输出保证一定是一个小数部分不为0的浮点数
样例输入
2
3.756
90.564
4543.5435
43.25
样例输出
94.32
4586.7935
思路
本题的思路和大整数的加法一样,重点在于如何解决小数点对齐的问题。
我这里改进了一下原来的大整数类型的结构体定义,分为整数部分和小数部分,然后用lend和lenf分别记录整数部分的长度和小数部分的长度。因此,在change函数读入字符串的时候,遇到小数点要跳过(其他部分差不多,也是逆序存储),在add函数里要先分情况给小数部分补0,如果某个数的小数部分长度较小,则把它的小数部分末尾一直添0,直到其lenf和那个数的lenf一样。
另外在实现加法的时候,方法还是一样,但是这里是先从小数部分加起,并且小数部分加完之后,不用先处理进位carry的问题,因为小数部分的进位肯定是加到整数部分的,所以代码不用改,再进行一遍整数部分的加法,等把整数部分的加法也完成了之后,再处理进位的问题。
这题总的来说思路简单,实现复杂,我因为一些细节上的问题也卡了很久。
这里总结一下本题的坑点:①小数部分的0是否输出的问题,一开始我的输出的算法是遇到0则跳过,这样就造成了如果是0.605则会输出0.65的问题,于是需要从低位开始遍历,记录第一个非0数的位置tempi,然后再从高位一直输出到tempi的位置即可;②函数里定义的暂存字符数组tmp一定要定义完之后初始化,要写成char tmp[150]={0}的形式,而不是char tmp[150]就完事了,这里我卡了很久很久,直到后来单步调试的时候才发现输入数据的顺序会影响tmp数组里的内容,进而导致后面补0的处理结果出错。
代码
#include
#include
#include
#include
#include
#include
using namespace std;
struct bign{
int d[150], f[150];//d是整数部分,f是小数部分
int lend, lenf;
bign(){
memset(d, 0, sizeof(d));
memset(f, 0, sizeof(f));
lend = lenf = 0;
}
};
bign change(char str[]){
bign a;
int doti = 0;
for(int i=strlen(str)-1;i>=0;i--){
if(str[i]=='.'){
doti = i;//记录小数点的位置
break;
}
else a.f[a.lenf++] = str[i]-'0';
}
for(int i=doti-1;i>=0;i--) a.d[a.lend++] = str[i]-'0';
return a;
}
bign add(bign a, bign b){
bign c;
int carry = 0;
if(a.lenf>b.lenf){
char tmp[150]={0};
int temp = a.lenf-b.lenf;//记录要补0的个数
for(int i=0;i<b.lenf;i++) tmp[i] = b.f[i]+'0';//把小数部分暂存在tmp里
b.lenf = 0;//暂时清空b小数部分的长度
for(int j=0;j<temp;j++) b.f[b.lenf++] = 0;
for(int i=0;i<strlen(tmp);i++) b.f[b.lenf++] = tmp[i]-'0';
}
else if(a.lenf<b.lenf){
char tmp[150]={0};
int temp = b.lenf-a.lenf;//记录要补0的个数
for(int i=0;i<a.lenf;i++) tmp[i] = a.f[i]+'0';//把小数部分暂存在tmp里
a.lenf = 0;//暂时清空a小数部分的长度
for(int j=0;j<temp;j++) a.f[a.lenf++] = 0;
for(int i=0;i<strlen(tmp);i++) a.f[a.lenf++] = tmp[i]-'0';
}
for(int i=0;i<a.lenf;i++){//a.lenf和b.lenf应该是相等的,写哪个都一样
int temp = a.f[i]+b.f[i]+carry;
c.f[c.lenf++] = temp%10;
carry = temp/10;
}
for(int i=0;i<a.lend||i<b.lend;i++){
int temp = a.d[i]+b.d[i]+carry;
c.d[c.lend++] = temp%10;
carry = temp/10;
}
if(carry!=0) c.d[c.lend++] = carry;
return c;
}
int main(){
int n;
while(scanf("%d", &n) != EOF){
while(n--){
char str1[150], str2[150];
scanf("%s", str1);
scanf("%s", str2);
bign x = change(str1);
bign y = change(str2);
bign z = add(x, y);
int tempi = 0;
for(int i=0;i<z.lenf;i++){
if(z.f[i]!=0){
tempi = i;//获得第一个非0的位置
break;
}
}
for(int i=z.lend-1;i>=0;i--) printf("%d", z.d[i]);
printf(".");
for(int i=z.lenf-1;i>=tempi;i--) printf("%d", z.f[i]);
printf("\n");
memset(str1, 0, sizeof(str1));
memset(str2, 0, sizeof(str2));
}
}
return 0;
}
题目描述
将M进制的数X转换为N进制的数输出。
输入
输入的第一行包括两个整数:M和N(2<=M,N<=36)。
下面的一行输入一个数X,X是M进制的数,现在要求你将M进制的数X转换成N进制的数输出。
输出
输出X的N进制表示的数。
样例输入
10 2
11
样例输出
1011
提示
注意输入时如有字母,则字母为大写,输出时如有字母,则字母为小写。
思路
这题和问题F类似(我先做的问题F),我直接沿用了F的函数(实在太多了记不住……),这里需要改一下的是change函数,如果读入的是字符A~Z,则转化成对应的10~35,另外,除N取余的时候,也需要判断一下,如果余数是10~35,则转化成a~z。
其他方面的大致思路就和普通的进制转换一样,先把M进制转换成10进制,再把10进制转换成N进制。转成10进制的时候需要用multi计算次方的问题,在这里,0次方的情况建议先直接加上去,否则循环条件也比较难写,计算完次方之后,还要用multi函数乘上当前的权值,然后把这些数加在一起就是对应的10进制数。10进制转N进制的话就要写得简单一些了,直接用while来除N取余一直循环即可,到商为0的时候break掉。
代码
#include
#include
#include
#include
#include
#include
using namespace std;
struct bign{
int d[1001];
int len;
bign(){
memset(d, 0, sizeof(d));
len = 0;
}
};
bign change(char s[]){
bign a;
a.len = strlen(s);
for(int i=a.len-1, j=0;i>=0, j<a.len;i--, j++){
if(s[i]>='A'&&s[i]<='Z') a.d[j] = s[i]-'A'+10;
else a.d[j] = s[i]-'0';
}
return a;
}
bign divide(bign a, int b, int& r){//a是被除数,b是除数,r余数
bign c;
c.len = a.len;
for(int i=a.len-1;i>=0;i--){
r = r*10+a.d[i];//r作为临时被除数
if(r<b) c.d[i] = 0;
else{
c.d[i] = r/b;//存放商
r = r%b;//存放余数
}
}
while(c.len-1>=1&&c.d[c.len-1]==0){
c.len--;
}
return c;
}
bign multi(bign a, int b){
bign c;
int carry = 0;
for(int i=0;i<a.len;i++){
int temp = a.d[i]*b+carry;
c.d[c.len++] = temp%10;
carry = temp/10;
}
while(carry!=0){
c.d[c.len++] = carry%10;
carry /= 10;
}
return c;
}
bign add(bign a, bign b){
bign c;
int carry = 0;
for(int i=0;i<a.len||i<b.len;i++){
int temp = a.d[i]+b.d[i]+carry;
c.d[c.len++] = temp%10;
carry = temp/10;
}
if(carry!=0) c.d[c.len++] = carry;
return c;
}
char str[1001]={0};
char tmp[1001]={0};//存放N进制数
int main(){
int M, N;
while(scanf("%d%d", &M, &N) != EOF){
scanf("%s", str);
bign X = change(str);
bign y;
bign temp;
temp.d[0] = X.d[0];
temp.len = 1;
y = add(y, temp);//先把最低位加上(因为不管是什么进制一定是0次方)
for(int i=1;i<X.len;i++){
bign z;
z.d[0] = 1;
z.len = 1;
for(int j=1;j<=i;j++){//计算M的i次方
z = multi(z,M);
}
z = multi(z,X.d[i]);//乘上权值
y = add(y, z);
}
int r = 0, index = 0;//r是余数
temp = divide(y, N, r);//除N取余
if(r>=10&&r<=35) tmp[index++] = r-10+'a';
else tmp[index++] = r+'0';
r = 0;
while(1){
temp = divide(temp, N, r);
if(r>=10&&r<=35) tmp[index++] = r-10+'a';
else tmp[index++] = r+'0';
r = 0;
if(temp.len==1&&temp.d[0]==0) break;
}
for(int i=strlen(tmp)-1;i>=0;i--) printf("%c", tmp[i]);//从高位输出
printf("\n");
memset(str, 0, sizeof(str));
memset(tmp, 0, sizeof(tmp));
}
return 0;
}
题目描述
对N个长度最长可达到1000的数进行排序。
输入
输入第一行为一个整数N,(1<=N<=100)。
接下来的N行每行有一个数,数的长度范围为1<=len<=1000。
每个数都是一个正数,并且保证不包含前缀零。
输出
可能有多组测试数据,对于每组数据,将给出的N个数从小到大进行排序,输出排序后的结果,每个数占一行。
样例输入
4
123
1234
12345
2345
样例输出
123
1234
2345
12345
思路
这题很简单,定义一个大整数的数组和字符串数组,把输入的字符串存放在字符串数组中,然后再用change函数转化成大整数类型放在大整数数组中,最后对这个大整数数组进行冒泡排序即可。
代码
#include
#include
#include
#include
#include
#include
using namespace std;
struct bign{
int d[1001];
int len;
bign(){
memset(d, 0, sizeof(d));
len = 0;
}
}tmp[101];
bign change(char s[]){
bign a;
a.len = strlen(s);
for(int i=a.len-1, j=0;i>=0, j<a.len;i--, j++) a.d[j] = s[i]-'0';
return a;
}
int judge(bign a, bign b){
if(a.len>b.len) return 1;//a大
else if(a.len<b.len) return -1;//b大
else{
for(int i=a.len-1;i>=0;i--){//长度相等从高位开始比较
if(a.d[i]>b.d[i]) return 1;
else if(a.d[i]<b.d[i]) return -1;
}
return 0;
}
}
char str[101][1001];
int main(){
int N;
while(scanf("%d", &N) != EOF){
for(int i=0;i<N;i++){
scanf("%s", &str[i]);
tmp[i] = change(str[i]);
}
///冒泡排序///
for(int i=1;i<=N-1;i++){
for(int j=0;j<N-i;j++){
if(judge(tmp[j], tmp[j+1])==1){
bign temp;
for(int k=0;k<tmp[j].len;k++) temp.d[k] = tmp[j].d[k];
temp.len = tmp[j].len;
for(int k=0;k<tmp[j+1].len;k++) tmp[j].d[k] = tmp[j+1].d[k];
tmp[j].len = tmp[j+1].len;
for(int k=0;k<temp.len;k++) tmp[j+1].d[k] = temp.d[k];
tmp[j+1].len = temp.len;
}
}
}
for(int i=0;i<N;i++){
for(int j=tmp[i].len-1;j>=0;j--) printf("%d", tmp[i].d[j]);
printf("\n");
}
}
return 0;
}
题目描述
对于一个十进制数A,将A转换为二进制数,然后按位逆序排列,再转换为十进制数B,我们称B为A的二进制逆序数。
例如对于十进制数173,它的二进制形式为10101101,逆序排列得到10110101,其十进制数为181,181即为173的二进制逆序数。
输入
一个1000位(即10999)以内的十进制数。
输出
输入的十进制数的二进制逆序数。
样例输入
985
样例输出
623
思路
这题要用到很多函数,虽然思路简单,但是代码比较复杂就是了。因为最多能到1000位,所以要用bign类型,但是,在10进制和2进制的互相转换之间,既需要乘法,又需要除法,而且最后在2进制转成10进制之后,还要用加法把所有大整数加起来。在读入的时候呢,又要把字符串转化成大整数,所以这题一共要四个函数:change、add、multi、divide(字符串类型转大整数类型、加法、乘法、除法)。
写完之后实现进制转换的方法就和低精度的数一样了,10进制转2进制需要除2取余,先得到的余数是低位,后得到的是高位,然后从高位输出。这里要注意,一次除法存储好余数之后,记得把余数清0,否则后续的结果会出错。此外,这里要的是逆序排列,所以直接从数组的i=0开始存放余数即可,得到的序列(从0~index-1)就是逆序后的序列。然后再对每一位1做下标次方的乘方运算(乘方运算就是用循环不断进行乘法运算),对0则直接跳过(否则会花费无用的时间),最后再加起来就是答案了。
另外,对于1需要特判输出为1,否则会多乘一次2,造成输入1的输出是2。
代码
#include
#include
#include
#include
#include
#include
using namespace std;
struct bign{
int d[1001];
int len;
bign(){
memset(d, 0, sizeof(d));
len = 0;
}
};
bign change(char s[]){
bign a;
a.len = strlen(s);
for(int i=a.len-1, j=0;i>=0, j<a.len;i--, j++) a.d[j] = s[i]-'0';
return a;
}
bign divide(bign a, int b, int& r){//a是被除数,b是除数,r余数
bign c;
c.len = a.len;
for(int i=a.len-1;i>=0;i--){
r = r*10+a.d[i];//r作为临时被除数
if(r<b) c.d[i] = 0;
else{
c.d[i] = r/b;//存放商
r = r%b;//存放余数
}
}
while(c.len-1>=1&&c.d[c.len-1]==0){
c.len--;
}
return c;
}
bign multi(bign a, int b){
bign c;
int carry = 0;
for(int i=0;i<a.len;i++){
int temp = a.d[i]*b+carry;
c.d[c.len++] = temp%10;
carry = temp/10;
}
while(carry!=0){
c.d[c.len++] = carry%10;
carry /= 10;
}
return c;
}
bign add(bign a, bign b){
bign c;
int carry = 0;
for(int i=0;i<a.len||i<b.len;i++){
int temp = a.d[i]+b.d[i]+carry;
c.d[c.len++] = temp%10;
carry = temp/10;
}
if(carry!=0) c.d[c.len++] = carry;
return c;
}
char str[1001]={0};
int tmp[1001]={0};//存放二进制数
int main(){
while(scanf("%s", str) != EOF){
if(strcmp(str, "1")==0) printf("1\n");//如果输入的是1,则特判输出为1
else{
int r = 0, index = 0;
bign x = change(str);
bign y;
bign temp = divide(x, 2, r);
tmp[index++] = r;//把余数存放在tmp里(从低位开始存放)
r = 0;
while(1){
temp = divide(temp, 2, r);
tmp[index++] = r;
r = 0;
if(temp.len==1&&temp.d[0]==0) break;//如果商为0,break
}
for(int i=0, j=index-1;i<index, j>=0;i++, j--){
if(tmp[i]==0) continue;//如果是0,直接进入下一轮循环
struct bign z;//定义一个大整数类型的z
z.d[0] = 1;//初始化为1
z.len = 1;
for(int k=1;k<=j;k++) z = multi(z,2);//计算2的j次方
y = add(y, z);
}
for(int i=y.len-1;i>=0;i--) printf("%d", y.d[i]);
printf("\n");
memset(str, 0, sizeof(str));
memset(tmp, 0, sizeof(tmp));
}
}
return 0;
}
大整数类型的题目,如果是单纯的加减的话,需要定义一个结构体,然后再写一个add函数或者sub函数,如果有需要最多再配一个change函数就好了,还是比较简单的,但是如果遇到大整数类型的进制转换,涉及到加法、乘法、除法,这么多一起写就比较麻烦了,思路虽然简单,但是过程是复杂的,而且也容易出错,这个时候思路一定要清晰,把进制之间的转化方法想清楚,基本上不会出太大的错误。
我个人认为大整数类型题目的难点还是写出相关的运算函数(表示记一个还记得住,要是让我一下子写三个的话会忘掉……)。