心得:
这题有几点需要注意:
1.刚开始担心进位的时候会超过当前进制数,如某位是二进制,会不会进位一个2?然后再相加的时候出现了4,那么向下一位进几?很纠结
后来看了别人的文章就发现这些担心是多余的
有这几个原则:
(1.)给出的数字不会超过这一位的进制数-1,例如有一位是5进制,这一位的数最高也就是4
(2.)和普通十进制加法一样,进位的数字要么1要么0,也就是说进给下一位的最大也就1;即使某一位加上了上一位进的1,得到的最大结果也只会向下一位进1
第一:这个很明显,单个数字不可能超过它的进制数-1,不然没意义
第二:先考虑第一位数,这个位相加没有加上上一位的进位(因为这是第一位,当然没有上一位),假设它是n进制,最大的数也就是n-1,两个n-1相加为2n-2,当相加结果为2n的时候才会向下一位进2,而结果却是2n-2,离2n还差2,所以第一位最大进1
假设第k位数向k+1位进1,k+1位的进制为n,两个最大的数n-1相加是2n-2,再加上进的位1结果是2n-1,也没到2n,它的进位也是1;所以进位最大是1,和最大也就是“十几”
2.应该对int和long long的范围敏感
int 4个字节 2^31-1 10位数 21开头
long long 8个字节 2^63-1 19位数 92开头
明显20位数相加,整型肯定不行了,必须用字符串,测试点3,4也如此
3.算加法时,n位数相加(最长的那个保证n位数),要考虑n+1位,因为还有第n位的进位
4.要想去除字符串前面一串的“0”:
(1.)可以使用stringstream转成整型,会自动去除,但缺点是长度不能超过整型的范围
(2.)无用的不好去除时,可以把有用的存到另一个地方,即遍历这个字符串,当发现第一个不是0的字符时,把剩下的字符串赋给另一个变量
(3.)也不一定要删除,可以只显示有用的,这样效果一样
bool find_no_zero=false;
for(int i=0;i<res.size();i++)
{
if(res.at(i)!='0'||find_no_zero)
{
find_no_zero=true;
cout<<res.at(i);
}
}
find_no_zero作用是防止中间的0没被显示输出,
这个写法思想就像 想进入有锁的房子,进去的方式是找到钥匙或者门是开着的,而有了钥匙就能使门打开
前面的0被"锁着"而不能输出,发现第一个不是零的字符就像找到了钥匙并打开了门,而后面的不管是什么都能被输出了,因为门已经开了,所以这个思想要熟记
题目:
地球人习惯使用十进制数,并且默认一个数字的每一位都是十进制的。而在 PAT 星人开挂的世界里,每个数字的每一位都是不同进制的,这种神奇的数字称为“PAT数”。每个 PAT 星人都必须熟记各位数字的进制表,例如“……0527”就表示最低位是 7 进制数、第 2 位是 2 进制数、第 3 位是 5 进制数、第 4 位是 10 进制数,等等。每一位的进制 d 或者是 0(表示十进制)、或者是 [2,9] 区间内的整数。理论上这个进制表应该包含无穷多位数字,但从实际应用出发,PAT 星人通常只需要记住前 20 位就够用了,以后各位默认为 10 进制。
在这样的数字系统中,即使是简单的加法运算也变得不简单。例如对应进制表“0527”,该如何计算“6203 + 415”呢?我们得首先计算最低位:3 + 5 = 8;因为最低位是 7 进制的,所以我们得到 1 和 1 个进位。第 2 位是:0 + 1 + 1(进位)= 2;因为此位是 2 进制的,所以我们得到 0 和 1 个进位。第 3 位是:2 + 4 + 1(进位)= 7;因为此位是 5 进制的,所以我们得到 2 和 1 个进位。第 4 位是:6 + 1(进位)= 7;因为此位是 10 进制的,所以我们就得到 7。最后我们得到:6203 + 415 = 7201。
输入格式:
输入首先在第一行给出一个 N 位的进制表(0 < N ≤ 20),以回车结束。 随后两行,每行给出一个不超过 N 位的非负的 PAT 数。
输出格式:
在一行中输出两个 PAT 数之和。
输入样例:
30527
06203
415
输出样例:
7201
思路:
先将两个数的前面填上足够的零,使它们的长度是进制表的长度
然后把某一位的两个数字和上一位的进位按照十进制相加,然后再转成对应的进制,转换后的结果十位作为下一位的进位,个位作为本位的结果,这是基本做法,但是还有一些细节
1.假如是n位数相加,那么结果要考虑n+1位,因为第n位的数相加时,可能会向下进一位,
比如测试用例
48527
37416
1
结果是100000,这也是测试点1的注意点
2.题目说最大数长度可能是20位的数,而(有符号)long long最长位19位,所以结果一定要用字符串输出,不然会出错
这也是测试点3 4的注意点,这两个测试点应该用的长数
3.输出结果要去掉字符串前面多余的0,并且如果结果字符串全是0的话,要输出一个0
比如测试用例
0
0
0
结果是0,这也是测试点5的注意点
代码:
#include<iostream>
#include<sstream>
using namespace std;
int change(int sum,int sys)
{//这个函数是将某数转成对应的进制,并返回一个整型结果
if(sum==0)
return 0;
string temp;
stringstream ss;
int val;
if(sys==0)//不要忘了0代表10进制,直接除0会错误
sys=10;
while(sum!=0)
{
temp.insert(0,1,(sum%sys)+'0');
sum/=sys;
}
ss<<temp;
ss>>val;
return val;
}
int main()
{
string table,num1,num2,res;
int index=0;
bool find_no_zero=false;
cin>>table>>num1>>num2;
int up[21]={};//存进位数的数组
num1.insert(0,table.size()-num1.size(),'0');//将数字在前面补零,补到和进制表一样长度
num2.insert(0,table.size()-num2.size(),'0');
for(int i=table.size()-1;i>=0;i--,index++)
{
int temp,i1=num1[i]-'0',i2=num2[i]-'0',sum=0;//i1,i2为对应字符转成的数字
sum=i1+i2+up[index];//两数和上一位的进位数相加
temp=change(sum,table[i]-'0');//然后转成对应进制
res.insert(0,1,temp%10+'0');//转换后的数个位作为本位结果
up[index+1]=temp/10;//十位作为向下一位的进位
}
res.insert(0,1,up[index]+'0');//不要忘了把最后一位的进位也算上
for(int i=0;i<res.size();i++)
{//不输出前面的0
if(res.at(i)!='0'||find_no_zero)
{
find_no_zero=true;
cout<<res.at(i);
}
}
if(!find_no_zero)//如果全是0,就输出0
cout<<0;
return 0;
}
另一种去掉前面的零的方法,这个做法是将不是0的字符存到另一个字符串里
bool all_zero=true;
for(int i=0;i<res_t.size();i++)
{
if(res_t.at(i)!='0')
{
all_zero=false;
for(int j=i;j<res_t.size();j++)
res+=res_t.at(j);
break;
}
}
if(all_zero)
cout<<"0"<<endl;
else
cout<<res<<endl;