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; }};
顺便说一句,在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使用说明请查看相应文档}}
Divided Land
Time Limit: 8000/4000 MS (Java/Others) Memory Limit: 65536/65536 K (Java/Others)Total Submission(s): 477 Accepted Submission(s): 253
Each case contains two binary number represents the length L and the width W of given land. (0 < L, W ≤ 21000)
310 100100 11010010 1100
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。
- ACM-高精度数
- 北大ACM 1001,关于高精度数的一些想法:
- ACM 96. [NOIP2007] 矩阵取数游戏(dp+高精度)
- ACM高精度运算
- ACM-高精度模板
- java----ACM高精度
- 【转】ACM高精度模板
- 大数(高精度数)模板
- 格式化高精度浮点数
- 求高精度幂数
- 浮点数高精度
- 高精度浮点数运算
- 高精度数算法 - - 加法
- 高精度catalan数模板
- 黄金分割数高精度计算
- Guess (浮点数,高精度)
- 删数游戏(高精度)
- ACM/ICPC Java高精度计算
- hashCode()、equals()以及compareTo()方法的理解
- splash界面代码
- Codeforces Round #269 (Div. 2) B. MUH and Important Things
- sgu107:987654321 problem
- myeclipse debug模式提示 source not found
- ACM-高精度数
- 对java中equals和hashCode函数的一些理解
- 如何学习一门新的编程语言
- HDU-2844-Coins(多重背包)
- java螺旋方阵实现
- LeetCode: Validate Binary Search Tree
- JavaSE 之 如何将一个用逗号分隔的字符串转换成字符数组
- 图形用户界面设计-基本控件 java实验报告第四个
- Android开发入门——Socket编程简单介绍