最近看《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