最近看《java核心技术卷I》,开篇几章就提到了BigInteger类,查了下相关文档,在不考虑其效率的时候,用作处理WOJ中的大整数还是挺有效的(BigInteger类可以存储任意大小的数)。
先链接两个比较好的博文:浅谈 BigInteger 再谈 BigInteger - 使用快速傅里叶变换
示例几个题目:
Problem 1142 - Half of and a Half Problem 1302 - Raising Modulo Numbers Problem 1315 - 高级机密
一、用JAVA BigInteger类处理上述题目
T1142 的意思其实很简单,由数列规律可以退出 a[n] = 2*a[n-1] +1 ,且a[0] = 1, 那么很容易推出 a[n]=2^(n+1)-1; 题目中 n的取值范围是n<=1000,那么用BigInteger实现就很简单:
package T_1142;
import java.math.BigInteger;
import java.util.Scanner;
public class Main {
public static void main(String[] args)
{
Scanner sc = new Scanner(System.in);
int n = 0;
while(sc.hasNext())
{
BigInteger BigResult = BigInteger.valueOf(2);
n = sc.nextInt();
if(n < 0)
return;
else
{
BigResult = BigResult.pow(n+1);
BigResult = BigResult.add(BigInteger.valueOf(-1));
System.out.println(BigResult);
}
}
}
}
T1302 中没有说Ai和Bi的取值范围,H和M的取值范围是 <=45000,用普通的数据类型肯定不能存储。用BigInteger也很容易实现:
package T_1302;
import java.math.BigInteger;
import java.util.Scanner;
public class main {
public static void main(String[] arg)
{
Scanner in = new Scanner(System.in);
int T = in.nextInt();
int a[] = new int[45005];
int b[] = new int[45005];
while(T>0)
{
BigInteger m = BigInteger.valueOf(in.nextInt());
int n = in.nextInt();
BigInteger result = BigInteger.valueOf(0);
for(int i=0; i<n; i++)
{
a[i] = in.nextInt();
b[i] = in.nextInt();
BigInteger Big_a = BigInteger.valueOf(a[i]);
BigInteger Big_b = BigInteger.valueOf(b[i]);
Big_a = Big_a.modPow(Big_b, m);
result = result.add(Big_a);
}
System.out.println(result.mod(m));
T--;
}
}
}
T1315 中用BigInteger提交时,超时(不理解上一题提交时不会超时,估计是取摸运算时候,上题的M<=45000,本题C<=2^30):
package T_1315;
import java.util.*;
import java.math.*;
public class main{
public static void main(String[] args)
{
Scanner in = new Scanner(System.in);
int a = in.nextInt();
int b = in.nextInt();
int c = in.nextInt();
while(!(a==0 && b==0 && c==0))
{
BigInteger Big_a = BigInteger.valueOf(a);
BigInteger Big_b = BigInteger.valueOf(b);
BigInteger Big_c = BigInteger.valueOf(c);
BigInteger result = BigInteger.valueOf(0);
if(c==0)
result = Big_a.pow(b);
else
result = Big_a.modPow(Big_b, Big_c);
System.out.println(result);
a = in.nextInt();
b = in.nextInt();
c = in.nextInt();
}
}
}
二、字符串数组表示大整数幂乘
T1142 由a[n] = 2*a[n-1]+1得知,采用字符串数组的形式表示 a[n] 数值的每一位,a[n]*2时只是将字符串数组的每一位转化为整数值乘2处理,注意进位:
/* Time: 2012.10.21
Number: WOJ 1142
Type: 大数值的处理 (用字符串表示大数值)
*/
#include <stdio.h>
#include <stdlib.h>
#include <string>
using namespace std;
int main()
{
char max[1000];
int N;
int i, j;
int temp;
int carry; // 进位
while(scanf("%d", &N) != EOF)
{
memset(max, '\0' ,sizeof(max));
max[0] = '1';
for(i=N; i>0; i--)
{
carry = 0;
j = 0;
while(max[j] != 0)
{
temp = (max[j] - '0')*2;
max[j] = (temp%10)+'0' + carry;
carry = temp/10;
j++;
}
if(carry != 0)
max[j] = carry + '0';
max[0] += 1;
}
for(i=strlen(max)-1; i>=0; i--)
{
j = max[i]-'0';
printf("%d", j);
}
printf("\n");
}
system("pause");
return 0;
}
三、幂乘法求解大整数幂乘
T1315 采用幂乘法, 计算大数值: a^b%c 时,可以采用公式 :
当b是偶数时,a^b%c = (a^(b/2))^2%c = ((a^2%c)^b/2)%c; 那么b/2可以设置为循环迭代结构,算法复杂度是 O(log(b));
当b是奇数时,a^b%c = {[(a^(b/2))^2]*a}%c = {[((a^2%c)^b/2)]*a}%c,主要原因其实就是 b = 2*(b/2) + 1; 注意每次迭代时都要注意b/2的奇偶性;
#include <stdio.h>
#include <stdlib.h>
int main()
{
long long int a,b,c;
long long int ans;
int flag = 0;
while(scanf("%lld%lld%lld",&a,&b,&c)==3 &&(a!=0 || b!=0 || c!=0))
{
ans = 1;
while(b>=1)
{
if(b%2==1)
ans = a*ans%c;
a=a*a%c;
b=b/2;
}
printf("%lld\n", ans);
}
system("pause");
return 0;
}
四、求100!中0的个数
组合数学老师给的一个题目,之前做的时候采用的是字符串数组依次存储计算结果,最后计算0的个数。《编程之美》有个更好的解法:对N!主要是将N!质因数分解,因为只有2*5才能产生10即一个0,那么就求分解后5的幂次方总和(该和小于2的幂次方和)。 详见编程之美--N的阶乘中末尾有几个0