题解 P2142 【高精度减法】
kernel_bin · · 题解
虽然我超菜,但这道题实在没有什么难度,主要是在这里介绍一种将高精度减法转换为高精度加法,巧妙避免减法借位的方法。 在特定情况下也能优化程序速度。
首先讲原理。 比如我们用253-76,假如直接做减法的话,从末尾开始减,3-6 = -3,需要向前面的5借位。5变成4,4-7=-3还需要借位,以此类推。最后结果是77
借位就很烦。有什么办法能避免借位呢? 其实,253 - 76 = 253 + (999 - 76) + 1 - 1000; 这个应该不难理解,补了1+999,最后减去1000. 这么做的好处是,999-76的过程不需要进行借位。只要直接减就行了。 这样,我们就把一次减法转化为一次加法,一次不需要借位的减法。(-1000那个可以直接通过某些方法消除进位)
下面贴代码,代码里也有注释。
稍稍要注意的是,我们要求被减数比减数更大。这也不难,简单判断一下,如果不行就对调减数和被减数,并标记负号就行了。
#include<stdio.h>
#include<stdlib.h>
typedef unsigned char BYTE;
BYTE a[10005] = { 0 };
BYTE b[10005] = { 0 };
BYTE *pa = 0, *pb = 0;
int la, lb;
int sign;//标明结果正负号用的。
int main()
{
scanf_s("%s", a,10005);
scanf_s("%s", b,10005);
//通过strlen得出长度,长的那个数字大。一样长比较首位,再一样继续循环第二位直到比出大小。(有一个优化,如果有一位一样那么可以直接消掉)
la = strlen((char *)a);
lb = strlen((char *)b);
//全部减去'0'
int i;
for (i = 0; i<la; i++)
{
a[i] -= '0';
}
for (i = 0; i<lb; i++)
{
b[i] -= '0';
}
if (la>lb)
{
//a大,正
sign = 1;
}
else if (la < lb)
{
sign = 0;//负号
}
else
{
//开始循环比较,有相等的就直接全部砍掉。
sign = -1;//标记。如果出来还是-1,说明两数相等
int BitCheckTag;
for (BitCheckTag = 0; BitCheckTag<la; BitCheckTag++)//这里la应该就是lb
{
if (a[BitCheckTag] > b[BitCheckTag])
{
//a > b
sign = 1;
break;
}
if (a[BitCheckTag] < b[BitCheckTag])
{
sign = 0;
break;
}
}
if (sign == -1)
{
printf("0");
return 0;
}
}
//这里应该已经标记好位数了。
if (sign == 1)
{
pa = a;
pb = b;
}
else
{
pa = b;
pb = a;
int buf;
buf = la;
la = lb;
lb = buf;
}
//现在pa就是大的那个,开始减吧
//为了避免借位,采用求补的方法。下面是例子
//253 - 76 = 253 + (999 - 76) + 1 -1000
//我们要做的是对减数求补,位数以被减数的为准。
//将 pb 后移,和 pa 的结尾对齐
memmove(pb + la - lb, pb, lb);
memset(pb, 0, la - lb);
//按la的长度给pb做求补
for (i = 0; i < la; i++)//la取不到?
{
pb[i] = 9 - pb[i];
}
pb[la - 1]++;
//补上加一。如果有超过10也不用管,和被减数相加的时候会处理的。
for (i = la - 1; i >= 0; i--)
{
pa[i] += pb[i];
if (pa[i] >= 10)
{
pa[i] -= 10;
if (i != 0)
{
pa[i - 1] ++;
}
}
}
for (i = 0; i<la; i++)
{
pa[i] += '0';
}
if (sign == 0)
{
printf("-");
}
//去除前导0
for (i = 0; i<la; i++)
{
if (pa[i] != '0')
{
printf((char *)pa + i);
return 0;
}
}
return 0;
}