ACM_大数运算 模板&&讲解&&各大oj题目

本文提供了一个大数运算的模板,涵盖了大数加法、减法、乘法、除法的原理和实现,并讲解了如何处理大数运算中的常见问题。同时,文章列举了各大在线判题平台(OJ)的相关练习题目,帮助读者加深理解和应用大数运算。
摘要由CSDN通过智能技术生成

///
作者:tt2767
声明:本文遵循以下协议自由转载-非商用-非衍生-保持署名|Creative Commons BY-NC-ND 3.0
查看本文更新与讨论请点击:http://blog.csdn.net/tt2767/article/details/45420067
链接被删请百度: CSDN t2767
///


本模板的代码中,大数加,减,乘,除,求幂以及阶乘已经通过各大oj,取模算法暂时没有做到大数运算的题,但本地测试正确。

如果你还不太明白大数运算,建议你拿一张白纸边看边试一下,并且本方法默认大数字符串初始化为‘\0’。

一. 大数加法的原理
想象一下我们手算加法的时候怎么算?比如说678 + 78 ,开始算的时候是第一个数的个位 + 第二个数的个位,也就是8+8=16,结果中的个位是6,而1要进位进到十位上去,我们可以设一个变量carry来代表进位的值,由于单个数相加的最大值为9+9=18 所以carry的只能是1或0 。再向下看,个位算完了应该算十位了,由于个位有进位,所以计算十位时是carry+7+7 == 1+7+7 = 15,这时十位的值为5,1要进位,carry=1;最后看百位,我们手算的时候由于78没有百位的值所以百位的值为carry+6,但是计算机对齐的时候由于初始化为‘\0’所以百位对齐的值是‘\0‘会造成计算错误,所以,在大数相加的时候应该对较小的数补充前导0使两数位对齐。如果是999+999这种情况,要考虑新增的位数,所以我们把数组转置,让低位在前,高位在后计算。

二. 大数减法
与大数加法类似,只不过,不需要对齐数位,把进位改成借位了。

三. 大数乘法
大数乘法的实现要调用大数加法,想一下手算的时候怎么算??还是算678*78,手算的时候会先算8*678=5424,然后算7*678=4746,但是4746会错开一位去写,因为这里的7其实是70所以4746真实值为47460,然后把两个结果相加得到的最后乘法运算的结果为5424+47460 = 52884
四. 大数除法
(这里说的除法是一个较大的数除以较小的数,其它的都可以转化成这种算法或直接得出结果)
大数除法是这里最耗费时间,也是最复杂的算法了。
因为我们几乎无法去模拟真实的手算,因为无法试除,但想一想除法的本质,除法的本质其实就是多个减法,但如果我们循环相减的话实在太浪费时间了,计数就是一个麻烦的问题。

那要怎么办呢?仍然是对齐法,只不过这次我们把最高位对齐,其他位补0,就是相当于把除数乘以10的某次幂使数位对齐这样就减少了减法的次数,我们还可以继续优化,对齐之后乘以某个数使除数接近于被除数,大家可以试一下这个数最小是被除数的最高位除以除数最高位与1的和即 (a[0]-‘0’)/(b[0]-‘0’+1),而最大值为9,这样我们就能算出一个最接近与被除数的数,而除数乘上的这两个值(10的某次幂与这个数)相乘就是商的一部分,我们让被除数减去这个计算出来接近被除数的值,然后仍旧让原除数对齐,新被除数,依次循环直至运算结束,每次累加出来的和就是商。说着有些绕,大家手算一下就明白了。举个例子:8000/99,先对齐99→9900,然而8000-9900得0,结果累加为0 ,进行下一步,99→990,990乘以9~0之间某个值最接近8000,这个值为8,故为8000-990*8=80,商累加为0+8*10=80,新被除数为80,99→99,然而无法再减,最后结果为80.

五. 大数除一个整形数
完全模拟手算就可以,和大数加减差不多,主要为了特殊情况节约时间;

六. 大数幂
使用二分求幂的方法去计算,需要调用大数乘法,二分幂详见http://blog.csdn.net/tt2767/article/details/45479003

七. 大数求模
大数求模,原理就是对每一位循环利用同余定理
如果 C = A + B
那么C % mod =( A % mod + B % mod)%mod 成立

八. 大数阶乘
这个输入一个整型数就可以了,输出结果为大数形式,按位计算,结果存在字符串中就好,类似于大数加减

九. 大数比较
大数比较就很简单了,主要了对比长度,然后从高位到低位对比每一位就好

十. 一些需要注意的事
每一个运算中都存在这WA点可能使你的程序得到错误的结果,看看你是否注意到了下面的一些情况?
1. 特殊值的处理:当值为0或1的时候怎么处理?正值与负值怎么处理?
2. 每次运算是否都把结果保存了下来?
3. 新运算之前是否清空了保存结果的缓存区?
4. 需不需要增加前导0?需不需要删去前导0?进位时怎么处理?
5. 是否破坏了原始数据,有没有把原始数据拷贝或还原?
6. 是否考虑了每一步计算对后面产生的影响?
如果你能处理好这些问题,那么大数计算对你来说就不是问题了
大家可以先写一写自己的算法,然后按照后边的题自己测试一下,你写的正确与否,再查看模板,模板中已经详细注释了,请仔细查看!

**模板在最后,详细解析在模板的注释里面!!**

各大OJ上的练习题:
1.大数的加减乘除
加:hdu 1002A+B Problem II http://acm.hdu.edu.cn/showproblem.php?pid=1002
减:百炼2736 http://bailian.openjudge.cn/practice/2736/
乘:百炼2980 http://bailian.openjudge.cn/practice/2980/
除:百炼2737 http://bailian.openjudge.cn/practice/2737/
除法把主程序改成这样,其他的直接输入输出就好~

int main()
{
    char a[N] = {
  '\0'},b[N]={
  '\0'};
    int n;
    scanf("%d",&n);
    while(n--)
    {
        scanf("%s%s",a,b);
        puts(BigDivBig(a,b));
    }
    return 0;
}

2.hdu 1042 N!
http://acm.hdu.edu.cn/showproblem.php?pid=1042
把模版里的主程序改成这样就过了 =。=

int main()
{
    int n;
    while(~scanf("%d",&n))
    {
        puts(BigFuc(n));
    }
    return 0;
}

3.poj3982 序列
http://poj.org/problem?id=3982
再把主程序改成这样 =。=

int main()
{
    char ans[105][N] = {
  '\0'};
    char temp[N]={
  '\0'};
    int n,i;

    while(~scanf("%s%s%s",ans[0],ans[1],ans[2]))
    {
        for(i = 3 ; i <=99 ; i++)
        {
            strcpy(temp,BigAdd(ans[i-3],ans[i-2]));
            strcpy(ans[i],BigAdd(temp,ans[i-1]));
        }
        puts(ans[99]);
    }
    return 0;
}

4 . NYOJ 73 比大小
http://acm.nyist.net/JudgeOnline/problem.php?pid=73
改主程序。。。。。

int main()
{
    char a[N] = {
  '\0'},b[N]={
  '\0'};
    int n;

    while(scanf("%s%s",a,b))
    {
        n = BigCmp(a,b);

        if(n == 0 && a[0] == '0' && b[0] == '0')
            break;

        if(n == 0)
            puts("a==b");
        else if(n == 1)
            puts("a<b");
        else
            puts("a>b");
    }
    return 0;
}

5.NYOJ 45 棋盘覆盖 (递推 + 大数)
http://acm.nyist.net/JudgeOnline/problem.php?pid=45
可以算出公式为 f[i] = 4 * f[i-1] + 1,用大数打个表加快速度

套用模板后,增加增加代表4和0的大数并修改主程序:

int main()
{
    char four[2] = {
  '4','\0'};//增加代表4和0的大数
    char one[2] = {
  '1','\0'};

    char ans[105][N] = {
  '\0'};
    char temp[N]={
  '\0'};
    int n,i,k,Case;
    ans[1][0] = '1';
    ans[1][1] = '\0';

    for(i = 2 ; i <= 100 ; i++)
    {
        strcpy(temp,BigMul(ans[i-1],four));
        strcpy(ans[i],BigAdd(temp,one));
    }
    scanf("%d",&Case);
    while(Case--)
    {
        scanf("%d",&k);
        puts(ans[k]);
    }
    return 0</
  • 9
    点赞
  • 38
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值