这是一道acm练习题,原题见“北京理工大学2012年ACM寒假练习题”,http://acm.bit.edu.cn/mod/programming/view.php?a=485
Problem Description
Now let’s calculate the answer of a + b ~
Input
The input will consist of a set of pairs of integers for a and b(-10^1000 <= a, b <= 10^1000). The input is ended by EOF.
Output
For each test case you should output the answer of a + b.
Sample Input
1 1
1 -1
Sample Output
20
看是很简单的题目,确花费了我一个晚上的时间才解出来,关键问题是:
1.这里面输入的数字远远超过了整形数据的最大范围,要求是-10^1000 <= a, b <= 10^1000,所以只能用字符串表示数字,就涉及到大整数运算问题 2.所有的测试用例都是隐藏的,我们看不到,所以必须考虑到所有可能的情况,比如 +0、-0、+123...... 3.写好的程序可以在线提交http://acm.bit.edu.cn/mod/programming/submit.php?a=485,这里是在线评测系统,有内存和计算时间限制(不能超过1s)
以下是本人的程序,仅供参考,速度有些低,算法很粗超,时间紧迫就没有添加注释,而且后期会做重大优化:
#include "stdafx.h"
#include <windows.h>
#include <string>
#include <iostream>
#include <vector>
using namespace std;
string AddNum(string sign, string a, string b)//加法运算,第一个参数表示结果符号,后两个为操作数
{
int carry = 0, i, j, k = -1;//carry表示进位标志位
if (sign == "-")//两个负数,则第0位为符号位
k = 0;
if (a.length() < b.length()) //a中始终存放最长的那个字符串
{
string tem = a;
a = b;
b = tem;
}
for (i = a.length() - 1, j = b.length() - 1; i >= k + 1 ; i--)//从尾向头计算
{
int n = (a[i] - '0') + ((j != k) ? (b[j--] - '0') : 0);
int d = carry + n;
if (d > 9)
{
carry = 1; //满10进1
d = d - 10;
}
else
carry = 0;
a[i] = d + '0'; //结果依然存放在a中
}
return (carry == 1 ? sign + "1" + a.substr(k + 1) : sign + a.substr(k + 1));
}
string Subtract(string a, string b)
{
string result = " ";
int i;
int f = 0;
string flag = "0";
if (a[0] == '-')
{
a = a.substr(1);
int le = a.length() - b.length();
if (le < 0)
{
for (i = 0; i < -1 * le; i++)
{
a.insert(0,"0");
}
}
if (le > 0)
{
for (i = 0; i < le; i++)
{
b.insert(0,"0");
}
flag = "-";
}
if (a == b)
{
return "0";
}
if (a < b)
{
flag = "0";
string tem = a;
a = b;
b = tem;
}
else
flag = "-";
}
else
{
b = b.substr(1);
int le = a.length() - b.length();
if (a == b)
{
return "0";
}
if (le < 0)
{
for (i = 0; i < -1 * le; i++)
{
a.insert(0,"0");
}
flag = "-";
string tem = a;
a = b;
b = tem;
}
else if (le > 0)
{
for (i = 0; i < le; i++)
{
b.insert(0,"0");
}
flag = "0";
}
else
{
if (a < b)
{
flag = "-";
string tem = a;
a = b;
b = tem;
}
else
flag = "0";
}
}
result = result + a;
for (i = a.length() - 1; i >= 0; i--)
{
if (a[i] < '0' || a[i] > '9' || b[i] < '0' || b[i] > '9')
{
return "error";
}
int n = (a[i] - '0') - (b[i] - '0');
int d = f + n;
if (d < 0)
{
f = -1;
d = d + 10;
}
else
f = 0;
result[i+1] = d + '0';
}
int m ;
for (m = 1; result[m] == '0'; m++);
result = result.substr(m);
if (flag[0] == '0')
return result;
else
return flag[0] + result;
}
string Calculate(string a, string b) //计算入口
{
if (a[0] != '-' && b[0] != '-') //如果是两个正数,符号位置为空
return AddNum("",a,b);
else if (a[0] == '-' && b[0] == '-') //如果是两个负数,符号位置为"-"
return AddNum("-",a,b);
else //两个异号数,执行减法运算
return Subtract(a,b);
}
int main()
{
string a,b;
vector<string> v;
unsigned int i = 0;
while (cin>>a)
v.push_back(a);
while(i < v.size())
{
cout<<Calculate(v[i], v[i+1])<<endl;
i = i + 2;
}
system("pause");
return 0;
}
2012-4.28编辑:
上面的算法有个最大的弊端就是运行需要占很大的内存,而且时间复杂度很好,但是易于理解,经过与其他同学的交流发现了一种更加简洁的方法:利用补码运算实现大数加法,其原理是模拟二进制补码运算,因为二进制数的减法可以通过对其补码求和实现,这样就可以把减法转换为加法运算,节省了大量的代码,也降低了时间复杂度和空间复杂度,请看代码:
#include "stdafx.h"
#include <windows.h>
#include <iostream>
#include <stdio.h>
#include <string.h>
using namespace std;
#define MAX 1010
char a[MAX+1];
char b[MAX+1];
char tem[MAX];
char sum[MAX+1];
void trans(char *a)//求十进制补码,最大数位1010
{
int i, x = 0;
for (i = MAX; ~i; --i)
{
a[i] = x - a[i];
x = -(a[i] < 0);
if (a[i] < 0) a[i] += 10;
}
cout<<a<<endl;
}
void plus(const char *a, const char *b, char *sum)
{
int i, x = 0;
for (i = MAX; ~i; --i) //对a和b的补码相加
{
sum[i] = a[i] + b[i] + x;
x = sum[i] >= 10;
if (sum[i] >= 10) sum[i] -= 10;
}
}
void output(char *a)
{
int i;
if(a[0] == 9)//表示结果是个负数
{
trans(a);//负数的补码即为原码
putchar('-');
}
for (i = 1; i < MAX && !a[i]; ++i);
while (i <= MAX) putchar(a[i++] + '0');
putchar('\n');
}
int input(char *a)
{
int i, j = MAX;
memset(a, 0, (MAX+1)*sizeof(char));
if(scanf("%s", tem)==EOF) return 0;
for(i = strlen(tem) - 1; ~i && tem[i]!='-' ; i--)
{
a[j--] = tem[i] - '0';
}
if(tem[0]=='-')trans(a); //如果是负数,求其补码
return 1;
}
int main()
{
while(input(a)&&input(b))
{
plus(a, b, sum);
output(sum);
}
system("pause");
return 0;
}
更详细的原理请参考 http://blog.youkuaiyun.com/jcwkyl/article/details/4090837
。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。上课去了