ACM-高精度数

来源:互联网 发布:dns劫持后的域名来路 编辑:程序博客网 时间:2024/04/27 12:00

在做题的过程中,我们经常会遇到这样一种情况,就是题目中给出的数据大的离谱,一般都是几十、上百位,毫无疑问,一般的数据类型如int、longlong、__int64等,是无法接收这些高精度数据的。很明显,如果不使用一些特殊的方法对这些数据进行处理,那就别想后面再写出什么算法来解决这道题!

说到这里,那到底用什么方法来接收这些大数据呢?答案是字符串,因为字符占用的空间比整型小。没错,就是先把数据当成字符串完整的接收下来,然后再对它进行处理。一般来说,我们的以字符串形式接收下来的对象,都会或多或少的参与运算,但是字符串游怎么参与运算呢?答案是重载运算符,按照运算规则重新实现各类运算。下面给出一份高精度类的模版,已经实现的运算有=、+、-、*、/、%、+=、-=、*=、/=、%=、power、sqrt、==、!=、>、<、>=、<=、>>、<<(可以继续扩展):

const int MAXN = 9999;class bign{public:    int len;          // 长度    int data[100];   // 控制大数的位数    bign () {len=1; memset(data, 0, sizeof(data));}   // 构造函数    bign (const int b)                                // 构造大数对象(整型)    {        int c,d = b;        len = 0;        memset(data, 0, sizeof(data));        while(d > MAXN)        {            c = d - (d / (MAXN + 1)) * (MAXN + 1);            d = d / (MAXN + 1);            data[len++] = c;        }        data[len++] = d;    }    bign (const char *s)                              // 构造大数对象(字符串)    {        int t,k,index,l,i;        memset(data, 0, sizeof(data));        l=strlen(s);        len=l/MAXN;        if(l%MAXN)            len++;        index=0;        for(i=l-1;i>=0;i-=MAXN)        {            t=0;            k=i-MAXN+1;            if(k<0)                k=0;            for(int j=k;j<=i;j++)                t=t*10+s[j]-'0';            data[index++]=t;        }    }    bign (const bign &T) : len(T.len)                 // 拷贝构造函数(大数)    {        int i;        memset(data, 0, sizeof(data));        for(i=0; i<len; ++i)            data[i] = T.data[i];    }    /*bign operator = (const bign &n)                 // 重载=(大数)    {        int i;        len = n.len;        memset(data,0,sizeof(data));        for(i = 0 ; i < len ; i++)            data[i] = n.data[i];        return *this;    }    bign operator = (const int num)                   // 重载=(整型)    {char s[MAXN];sprintf(s, "%d", num);*this = s;return *this;    }    bign operator = (const char *num)                 // 重载=(字符串)    {for(int i=0; num[i]=='0'; ++num);     // 去前导0len = strlen(num);for(int i = 0; i < len; ++i)            data[i] = num[len-i-1] - '0';return *this;    }*/    bign operator + (const bign &T)                  // 重载+(大数)    {        bign t(*this);        int i, big;      //位数        big = T.len > len ? T.len : len;        for(i = 0 ; i < big ; i++)        {            t.data[i] += T.data[i];            if(t.data[i] > MAXN)            {                t.data[i + 1]++;                t.data[i] -=MAXN+1;            }        }        if(t.data[big] != 0)            t.len = big + 1;        else            t.len = big;        return t;    }    bign operator - (const bign &T)                 // 重载-(大数),注意需要大减小    {        int i,j,big;        bool flag;        bign t1,t2;        if(*this>T)        {            t1=*this;            t2=T;            flag=0;        }        else        {            t1=T;            t2=*this;            flag=1;        }        big=t1.len;        for(i = 0 ; i < big ; i++)        {            if(t1.data[i] < t2.data[i])            {                j = i + 1;                while(t1.data[j] == 0)                    j++;                t1.data[j--]--;                while(j > i)                    t1.data[j--] += MAXN;                t1.data[i] += MAXN + 1 - t2.data[i];            }        else            t1.data[i] -= t2.data[i];        }        t1.len = big;        while(t1.data[t1.len - 1] == 0 && t1.len > 1)        {            t1.len--;            big--;        }        if(flag)            t1.data[big-1]=0-t1.data[big-1];        return t1;    }    bign operator * (const bign &T)           // 重载*(大数)    {        bign ret;        int i,j,up;        int temp,temp1;        for(i = 0 ; i < len ; i++)        {            up = 0;            for(j = 0 ; j < T.len ; j++)            {                temp = data[i] * T.data[j] + ret.data[i + j] + up;                if(temp > MAXN)                {                    temp1 = temp - temp / (MAXN + 1) * (MAXN + 1);                    up = temp / (MAXN + 1);                    ret.data[i + j] = temp1;                }                else                {                    up = 0;                    ret.data[i + j] = temp;                }            }            if(up != 0)                ret.data[i + j] = up;        }        ret.len = i + j;        while(ret.data[ret.len - 1] == 0 && ret.len > 1)            ret.len--;        return ret;    }    bign operator / (const int &b)            // 重载/(整型)    {        bign ret;        int i, down=0;        for(i = len - 1 ; i >= 0 ; i--)        {            ret.data[i] = (data[i] + down * (MAXN + 1)) / b;            down = data[i] + down * (MAXN + 1) - ret.data[i] * b;        }        ret.len = len;        while(ret.data[ret.len - 1] == 0 && ret.len > 1)            ret.len--;        return ret;    }    bign operator / (const bign &b)           // 重载/(大数)    {bign c, f = 0;for(int i=len-1; i>=0; --i){f = f*10;f.data[0] = data[i];while(f >= b){f -= b;c.data[i]++;}}c.len = len;c.clean();return c;    }    int operator % (const int &b)             // 重载%(整型)    {        int i,d=0;        for (i = len-1; i>=0; i--)        {            d = ((d * (MAXN+1))% b + data[i])% b;        }        return d;    }    bign operator >> (const int &k)    {        bign t(*this);        int p = k;        while(p)        {            t = t/2;            p--;        }        return t;    }    bign operator << (const int &k)    {        bign t(*this);        int p = k;        while(p)        {            t = t*2;            p--;        }        return t;    }    bign operator += (const bign &b)          // 重载+=(大数)    {*this = *this + b;return *this;    }    bign operator -= (const bign &b)          // 重载-=(大数){*this = *this - b;return *this;}    bign operator *= (const bign &b)          // 重载*=(大数){*this = *this * b;return *this;}    bign operator /= (const bign &b)          // 重载/=(大数){*this  = *this / b;return *this;}    bign operator %= (const int &b)           // 重载%=(整型){*this = *this % b;return *this;}    bool operator == (const bign &T)          // 重载==(大数)    {        if(len!=T.len) return false;        int ln = len-1;        int flag = 0;        while(ln >= 0 && data[ln] == T.data[ln]) ln--;        if(ln == -1) return true;        return false;    }    bool operator == (const int &t)           // 重载==(整型)    {        bign b(t);        return *this==b;    }    bool operator != (const bign &b)          // 重载!=(大数)    {return !(*this == b);    }    bool operator > (const bign &T)           // 重载>(大数)    {        int ln;        if(len > T.len)            return true;        else if(len == T.len)        {            ln = len - 1;            while(ln >= 0 && data[ln] == T.data[ln])                ln--;            if(ln >= 0 && data[ln] > T.data[ln])                return true;            else                return false;        }        else            return false;}    bool operator > (const int &t)            // 重载>(整型)    {        bign b(t);        return *this>b;    }    bool operator < (const bign &T)           // 重载<(大数)    {        if(!(*this>T)&&!(*this==T)) return true;        return false;    }    bool operator < (const int &t)            // 重载<(整型)    {        bign b(t);        return *this<b;    }    bool operator <= (const bign &b)          // 重载<=(大数)    {        return *this < b || *this == b;    }    bool operator >= (const bign &b)          // 重载>=(大数)    {        return *this > b || *this == b;    }    bign power(const int &n)                  // 大数的n次方运算    {        bign t,ret(1);        int i;        if(n<0)            exit(-1);        if(n==0)            return 1;        if(n==1)            return *this;        int m=n;        while(m>1)        {            t=*this;            for( i=1;i<<1<=m;i<<=1)            {                t=t*t;            }            m-=i;            ret=ret*t;            if(m==1)                ret=ret*(*this);        }        return ret;    }    typedef int bignum_t[MAXN+1];    void sub(bignum_t a,const bignum_t b,const int c,const int d)    {       int i,O=b[0]+d;       for (i=1+d;i<=O;i++)              if ((a[i]-=b[i-d]*c)<0)                     a[i+1]+=(a[i]-MAXN+1)/MAXN,a[i]-=(a[i]-MAXN+1)/MAXN*MAXN;       for (;a[i]<0;a[i+1]+=(a[i]-MAXN+1)/MAXN,a[i]-=(a[i]-MAXN+1)/MAXN*MAXN,i++);       for (;!a[a[0]]&&a[0]>1;a[0]--);    }    int comp(const bignum_t a,const int c,const int d,const bignum_t b)    {       int i,t=0,O=-MAXN*2;       if (b[0]-a[0]<d&&c)            return 1;       for (i=b[0];i>d;i--)       {              t=t*MAXN+a[i-d]*c-b[i];              if (t>0) return 1;              if (t<O) return 0;       }       for (i=d;i;i--)       {              t=t*MAXN-b[i];              if (t>0) return 1;              if (t<O) return 0;       }       return t>0;    }    void sqrt(bignum_t b,bignum_t a)          // 计算b的a次方根    {       int h,l,m,i;       memset((void*)b,0,sizeof(bignum_t));       for(i=b[0]=(a[0]+1)>>1;i;sub(a,b,m,i-1),b[i]+=m,i--)            for(h=MAXN-1,l=0,b[i]=m=(h+l+1)>>1;h>l;b[i]=m=(h+l+1)>>1)                if(comp(b,m,i-1,a)) h=m-1;                    else l=m;       for(;!b[b[0]]&&b[0]>1;b[0]--);       for(i=1;i<=b[0];b[i++]>>=1);    }    string str ()                             // 将内部的字符数组转换成string并返回    {string res = "";for(int i=0; i<len; ++i)            res = char(data[i]+'0') + res;return res;    }    void clean()    {        while(len>1 && !data[len-1]) len--;    }    // 重载输入、输出流,这样我们可以更方便操作高精度对象    friend istream& operator >> (istream &in, bign &b)    {        char ch[MAXN*4];        int i = -1;        in>>ch;        int l=strlen(ch);        int count=0,sum=0;        for(i=l-1;i>=0;)        {            sum = 0;            int t=1;            for(int j=0;j<4&&i>=0;j++,i--,t*=10)            {                sum+=(ch[i]-'0')*t;            }            b.data[count]=sum;            count++;        }        b.len =count++;        return in;    }    friend ostream& operator << (ostream &out, bign &b)    {        int i;        cout << b.data[b.len - 1];        for(i = b.len - 2 ; i >= 0 ; i--)        {            cout.width(MAXN);            cout.fill('0');            cout << b.data[i];        }        return out;    }};


上面的bign的这个高精度类已经实现了大部分的运算种类,但是如果有其他特殊运算,也可以继续扩展,以后我也会继续更新。说实话,bign类看起来比较庞大,但是其实我们在写代码时,只需要挑出真正需要的运算就可以了。

顺便说一句,在Java中,已经有现成的高精度类可以使用了,它们是大整型类BigInteger,大浮点型类BigDecimal,但是作为搞算法的我们来说,了解它们的实现还是有必要的,作为扩展,我下面还是给出Java的模版吧,详细的应用在这里(点击打开链接):

import java.math.*;  // BigDecimal和BigInteger两个类,都包含在java.math.*里面public class Main {public static void main(String[] args){BigInteger x = new BigInteger("123456789123456789123456789");        BigInteger y = new BigInteger("123456789123456789123456789");        System.out.println(x .add(y));  // 加        /*BigInteger其它常用函数:        subtract(BigInteger val)        减        multiply(BigInteger val)        乘        divide(BigInteger val)          除        abs()                           绝对值        compareTo(BigInteger val)       比较        pow(int exponent)               幂        gcd(BigInteger val)             最大公约数        mod(BigInteger val)             模        */                BigDecimal p = new BigDecimal("123456789123456789123456789.123456789");        BigDecimal q = new BigDecimal("123456789123456789123456789.123456789");        System.out.println(x .add(y));        /*BigDecimal其它常用函数:        BigDecimal(String val) 将 BigDecimal 的字符串表示形式转换为 BigDecimal。        abs() 返回 BigDecimal,其值为此 BigDecimal 的绝对值,其标度为 this.scale()。        add(BigDecimal augend) 返回一个 BigDecimal,其值为 (this + augend),其标度为 max(this.scale(), augend.scale())。        compareTo(BigDecimal val) 将此 BigDecimal 与指定的 BigDecimal 比较。        setScale(int newScale, RoundingMode roundingMode)  返回 BigDecimal,其标度为指定值,其非标度值通过此 BigDecimal 的非标度值乘以或除以十的适当次幂来确定,以维护其总值。        subtract(BigDecimal subtrahend)  返回一个 BigDecimal,其值为 (this - subtrahend),其标度为 max(this.scale(), subtrahend.scale())。        divide(BigDecimal divisor, RoundingMode roundingMode) 返回一个 BigDecimal,其值为 (this / divisor),其标度为 this.scale()。        RoundingMode.            CEILING      向正无限大方向舍入的舍入模式。            DOWN         向零方向舍入的舍入模式。            FLOOR        向负无限大方向舍入的舍入模式。            HALF_DOWN    向最接近数字方向舍入的舍入模式,如果与两个相邻数字的距离相等,则向下舍入。            HALF_EVEN    向最接近数字方向舍入的舍入模式,如果与两个相邻数字的距离相等,则向相邻的偶数舍入。            HALF_UP      向最接近数字方向舍入的舍入模式,如果与两个相邻数字的距离相等,则向上舍入。            UNNECESSARY  用于断言请求的操作具有精确结果的舍入模式,因此不需要舍入。            UP           远离零方向舍入的舍入模式。        */    // 详细API使用说明请查看相应文档}}


接下来就拿一道题来写一下,HDOJ:5050,时空转移(点击打开链接),题目如下:

Divided Land

Time Limit: 8000/4000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others)
Total Submission(s): 477    Accepted Submission(s): 253


Problem Description
It’s time to fight the local despots and redistribute the land. There is a rectangular piece of land granted from the government, whose length and width are both in binary form. As the mayor, you must segment the land into multiple squares of equal size for the villagers. What are required is there must be no any waste and each single segmented square land has as large area as possible. The width of the segmented square land is also binary.
 

Input
The first line of the input is T (1 ≤ T ≤ 100), which stands for the number of test cases you need to solve.

Each case contains two binary number represents the length L and the width W of given land. (0 < L, W ≤ 21000)
 

Output
For each test case, print a line “Case #t: ”(without quotes, t means the index of the test case) at the beginning. Then one number means the largest width of land that can be divided from input data. And it will be show in binary. Do not have any useless number or space.
 

Sample Input
310 100100 11010010 1100
 

Sample Output
Case #1: 10Case #2: 10Case #3: 110
 

题意:

就是说给你一块长方形,告诉你长和宽,并且都是以二进制表示的,然后在没有浪费的情况下,将它切成多个相同的正方形,最后让求正方形可能的最大边长,答案也要用二进制表示。

分析:

拿到题后,我们首先发现的是数据的极值尽然是2^1000,这个数大约是10^300,于是便自然而然想到了高精度类。这样,数据存储便搞定了,但是数据却是以二进制表示的,这样似乎让我们摸不着头脑,那先假设数据是十进制。然后题目是要将长方形划分为正方形,又不能有浪费,很显然正方形边长只有能将长方形长和宽整除才能满足条件,即边长是长和宽的公约数,并且又要保证边长最大,那就最大公约数哇!但是,我们熟知的gcd算法只能处理一般的数据,即范围不大的十进制数,但是本题中数据是高精度,那么就涉及到高精度的gcd算法,具体方法看另一篇文章,传送门(点击打开链接);而对于二进制的问题,它与十进制之间的转换是比较方便的,因为只涉及到2的幂次,具体看代码。本题代码如下:

#include <iostream>#include <string>#include <cstring>#include <cstdlib>#include <cstdio>using namespace std;const int MAXN = 9999;class bign{    // 如前面所示的大数类};bool iseven (bign x){    if(x.data[0]%2==0)        return true;    return false;}bign gcd (bign a, bign b){    bign k=1, ans;    while (1)    {        if (a == 0)        {ans = b; break;}        if (b == 0)        {ans = a; break;}        if (a < b)        {            bign temp = a;            a = b;            b = temp;        }        else        {            if (iseven(a))            {                if (iseven(b))                {                    a = a/2;                    b = b/2;                    k=k*2;                }                else a = a/2;            }            else            {                if (iseven(b)) b = b/2;                else a = (a-b)/2;            }        }    }    return ans*k;}int main(){//freopen("sample.txt","r",stdin);    int t, i, j, cas=1;    char a[2005], b[2005];    scanf("%d", &t);    while (t--)    {        scanf ("%s %s", a, b);        bign temp(1), t1(0), t2(0);        int len = strlen(a);        for (i=len-1; i>=0; --i)        {            if(a[i] == '1')       // 从二进制最右边的一位累加,如111=4+2+1                t1 = t1 + temp;            temp = temp*2;        }        len = strlen(b);        temp = 1;        for (i=len-1; i>=0; --i)   // 将b也转换为十进制        {            if(b[i] == '1')                t2 = t2+temp;            temp = temp*2;        }        int tt[2005];        bign ans = gcd(t1, t2);        int k = 0;        while (ans != 0)          // 将十进制转换为二进制        {            if(ans%2 == 1)                tt[k++] = 1;            else tt[k++] = 0;            ans = ans / 2;        }        printf("Case #%d: ", cas++);        for (i=k-1; i>=0; --i)            printf("%d", tt[i]);        printf("\n");    }    return 0;}

下面顺便也把Java写的代码贴出来吧,因为它的确是很简便:

import java.math.*;import java.util.Scanner;public class Main {public static void main(String[] args) {Scanner cin = new Scanner(System.in);BigInteger a, b;int cas = cin.nextInt();for(int i=1; i<=cas; ++i){a = cin.nextBigInteger(2);   // 以二进制输入b = cin.nextBigInteger(2);a = a.gcd(b);System.out.println("Case #"+i+": "+a.toString(2));  // 以二进制输出}}}

大数到这就告一段落了,现在再附上几道题,以作为练习,HDOJ:1042、1250、3546。


0 0
原创粉丝点击