一、题目大意¶
给定\(n\)组数据a,b,求出\(a \times b\)。
其中\(n \leq 500, 0 \leq a,b \leq 10^{5000}\)。
二、算法选择¶
设相乘的数的位数最多为\(m\),则朴素算法的复杂度为\(O(nm^2)\),在本题的数据范围下会造成超时。
于是考虑进行压缩位数的操作,假设压缩\(k\)位。为了防止在运算过程中压位存储地数组元素出现溢出,\(k\)的取值需要满足不等式
解得\(k <= 8, k \in \mathbb{Z}\)。同时,显然压位后的算法时间复杂度为\(\displaystyle O(\frac{m^2}{k^2})\),其中\(m\)为两个乘数的最大位数。因此,\(k\)需要尽可能地取大。因此我们选择\(k=8\),即压八位进行存储(采用\(10^8\)进制),并进行后续的运算。
三、代码实现¶
为此我们定义函数change_to_number,从最末尾开始,使用strncpy函数每八位截取一段,通过atoi()函数转换为八位正整数,顺序存放在数组n中,并返回最终数组n的位数,也即代码中的now。
const int w = 8;//压缩存储的位数
int change_to_number(char s[], long long n[]) {
char temp[MAX];
int len = strlen(s);
int now = 0;
while(len / w){
strncpy(temp,s + len - w, w);
n[now] = atoi(temp);
len -= w;
now ++;
}
if(len){
memset(temp, 0, sizeof(temp));
strncpy(temp, s, len);
n[now] = atoi(temp);
now ++;
}
return now;
}
定义函数mul,对两个处理好的存放着八位整数的数组a,b进行模拟竖式乘法运算,合理处理进位,获得结果。
const int jw = 1e8;//压位算法下的进制
void mul(long long a[], long long b[], long long c[], int m, int n) {
for(int i = 0;i < m;i++){
for(int j = 0;j < n;j++){
c[i + j] += a[i] * b[j];
c[i + j + 1] += c[i + j] / jw;
c[i + j] = c[i + j] % jw;
}
}
return;
}
定义函数print_out(),对结果数组进行输出。从第一个不是0的一位开始,依次向前输出,不足八位的采用printf("%0*d")的方法补足0,这样就完成了对结果的输出。此处需要特殊判断,若结果为0,则手动输出0。
void print_out(long long a[]) {
bool check = false;
for(int i = 10004;i >= 0;i--){
if(a[i] != 0){
check = true;
printf("%d", a[i]);
for(int j = i - 1;j >= 0;j--){
printf("%0*d", w, a[j]);
}
printf("\n");
break;
}
}
if(check == false) {
cout << "0" << endl;
}
}
四、复杂度分析¶
本题中,设每次计算的两个乘数的位数为\(p,q\),算法的时间复杂度为\(O(pq)\),空间复杂度为\(O(p+q)\)。
评论