数位拆解
- 例4.1 特殊乘法
#include<stdio.h>
int main(){
int a,b;
while(scanf("%d%d", &a, &b) != EOF){
int buf1[20], buf2[20], size1 = 0, size2 = 0;
while (a!=0){
buf1[size1++] = a % 10;
a /= 10;
}
while (b!=0){
buf2[size2++] = b % 10;
b /= 10;
}
int ans = 0;
for (int i=0;i<size1;i++){
for (int j=0;j<size2;j++){
ans += buf1[i] * buf2[j];
}
}
printf("%d", ans);
}
return 0;
}
进制转换
- 例4.2 输出 A+B 的 m 进制数
使用 long long 来防止溢出。
#include<stdio.h>
int main(){
int m;
long long a, b; // 确保不会溢出
while (scanf("%d", &m) != EOF){
if(m == 0)
break;
scanf("%lld%lld", &a, &b);
a = a + b;
int ans[50], size = 0;
// while (a!=0){
// ans[size++] = a % m;
// a /= m;
// }
// 需要用 do while 循环, 防止测试用例出现 0
do{
ans[size++] = a % m;
a /= m;
}while(a!=0);
for (int i=size-1;i>=0;i--){
printf("%d", ans[i]);
}
printf("\n");
}
return 0;
}
- 例4.3 数制转换
将 n 进制数 a,转换成 b 进制数:首先将 n 进制转化成十进制,然后将此十进制转化为 b 进制。
#include<stdio.h>
#include<string.h>
int main(){
int a, b;
char str[40];
while(scanf("%d%s%d", &a, str, &b) != EOF){
int tmp = 0, length = strlen(str), c = 1;
for (int i=length-1;i>=0;i--){
int x;
if (str[i] >= '0' && str[i] <= '9'){
x = str[i] - '0';
}
else if (str[i] >= 'a' && str[i] <= 'b'){
x = str[i] - 'a' + 10;
}
else
x = str[i] - 'A' + 10;
tmp += x * c;
c *= a;
}
char ans[40], size = 0;
do{
int x=tmp % b;
ans[size++] = (x<10) ? x+'0':x-10+'A';
tmp /= b;
}while(tmp!=0);
for(int i=size-1;i>=0;i--){
printf("%c", ans[i]);
}
printf("\n");
}
return 0;
}
最大公约数
a,b的最大公约数同时也是 b,a mod b 的最大公约数。
我们不断重复这个过程,直到出现某个非0数和0,则该非0数即为最大公约数。
例4.4 最大公约数
#include<stdio.h>
int gcd(int a, int b){
if (b==0)
return a;
else
return gcd(b, a%b);
}
int main(){
int a,b;
while (scanf("%d%d", &a, &b) != EOF){
printf("%d\n", gcd(a,b));
}
return 0;
}
最小公倍数
a,b两数的最小公倍数为两数的乘积除以他们的最大公约数。
- 例4.5 最小公倍数
#include<stdio.h>
int gcd(int a, int b){
return b!=0 ? gcd(b, a%b):a;
}
int main(){
int a,b;
while(scanf("%d%d", &a, &b) != EOF){
printf("%d\n", a*b/gcd(a,b));
}
return 0;
}
素数筛法
判断整数 n 是否为素数,我们只需测试到 sqrt(n) 即可。若 n 不存在大于 sqrt(n) 的因数时,显然正确,若存在大于等于 sqrt(n) 的因数 y,则 z=n/y 必同时为 n 的因数,且其值小于等于 sqrt(n),所以我们只需要测试到 sqrt(n)。
如何找出 0-1000000 中的所有素数?可以采用素数筛法,在获得一个素数时,将它所有的倍数都标记为非素数。
- 例4.7 素数
#include<stdio.h>
int prime[10000]; //保存筛选出的素数
int primeSize;
bool mark[10001]; //若 mark[x] 为 true ,则表示该数 x 已被标记成非素数
void init(){
for (int i=0;i<=10000;i++){
mark[i] = false;
}
primeSize = 0;
for (int i=2;i<=10000;i++){
if (mark[i] == true)
continue;
prime[primeSize++] = i;
for (int j=i*i;j<=10000;j+=i){
mark[j] = true;
}
}
}
int main(){
init();
int n;
while(scanf("%d", &n) != EOF){
bool isOutput = false;
for(int i=0;i<primeSize;i++){
if(prime[i] < n && prime[i] % 10 == 1){
if(isOutput == false){
isOutput = true;
printf("%d", prime[i]);
}
else
printf(" %d", prime[i]);
}
}
if(isOutput == false)
printf("-1\n");
else
printf("\n");
}
return 0;
}
n
分解素因数
对一个 //(x//) 分解素因数即确定素数 //(p_1, p_2,…p_n//) 使其满足下式:
$$x = p_1^{e1}p_2^{e2}…p_n^{en}$$
除了素数,还要确定幂指数。
- 例4.8 质因数的个数
#include<stdio.h>
bool mark[100001];
int prime[100001];
int primeSize;
void init(){
primeSize = 0;
for (int i=2;i<=10000;i++){
if (mark[i] == true)
continue;
prime[primeSize++] = i;
for (int j=i*i;j<=10000;j+=i){
mark[j] = true;
}
}
}
int main(){
init();
int n;
while (scanf("%d", &n) != EOF){
int ansPrime[30];
int ansSize = 0;
int ansNum[30];
for(int i =0;i<primeSize;i++){
if(n%prime[i] == 0){
ansPrime[ansSize] = prime[i];
ansNum[ansSize] = 0;
while(n%prime[i] == 0){
ansNum[ansSize]++;
n /= prime[i];
// printf("%d", prime[i]);
}
ansSize++;
if (n==1)
break;
}
}
if(n!=1){
ansPrime[ansSize] = n;
ansNum[ansSize++] = 1;
}
int ans = 0;
for(int i=0;i<ansSize;i++){
ans += ansNum[i];
}
printf("%d\n", ans);
}
return 0;
}
- 例4.9 整除问题
给定n,a 求最大的 k 使得 n! 可以被 a^k 整除但不能被 a^(k+1) 整除。
n! 和 a^k 可能非常大,甚至不能用 long long 类型来保存,无法直接求余判断是否存在整除关系
二分求幂
- 例4.10 人见人爱 A^B
求 A^B 的最后三位数表示的整数,A^B可能非常大,不容易保存,而我们注意到 A^B 的后三位数只与 A 的后三位数和 B 有关,所以我们在计算过程中只需要保存中间结果的后三位。
以2^31为例,了解二分求幂:
$$2^{31}=2^12^22^42^82^{16}$$
#include<stdio.h>
int main(){
int a,b;
while(scanf("%d%d", &a, &b) != EOF){
if (a == 0 && b == 0)
break;
int ans = 1;
while(b!=0){
if(b%2 == 1){
ans *= a;
ans %= 1000;
}
b /= 2;
a *= a;
a %= 1000;
}
printf("%d\n", ans);
}
return 0;
}
高精度整数
我们通常用一个结构体来保存高精度整数:
struct bigInteger{
int digit[1000];
int size;
}
digit 数组用来保存大整数中每若干位的数字,我们这里使用每 4 位为一个单位保存,size 为 digit数组中第一个我们还没使用过的数组单元,如 123456789,digit[0]=6789,digit[1]=2345,digit[2]=1,size=3。
- 例4.11 a+b
#include<stdio.h>
#include<string.h>
struct bigInteger{
// 高精度整数结构体
int digit[1000];
int size; //下一个我们未使用的数组单元
void init(){
for (int i=0;i<1000;i++){
digit[i] = 0;
size = 0;
}
}
void set(char str[]){
init();
int L = strlen(str);
for (int i=L-1, j=0, t=0, c=1;i>=0;i--){
// j控制每四个字符转换为一个数字,t临时保存字符转换为数字的中间值,c 表示当前位的权重
t += (str[i] - '0') * c;
j++;
c *= 10;
if (j == 4 || i == 0){
c = 1;
digit[size++] = t;
j = 0;
t = 0;
}
}
}
void output(){
for (int i=size-1;i>=0;i--){
if (i!=size-1)
printf("%04d", digit[i]);
else
printf("%d", digit[i]);
}
}
bigInteger operator + (const bigInteger &A) const{
// 加法运算符重构
bigInteger ret;
ret.init();
int jinwei = 0; // 进位
for (int i=0;i<A.size || i<size;i++){
int tmp = A.digit[i] + digit[i] + jinwei;
jinwei = tmp / 10000;
ret.digit[ret.size++] = tmp % 10000;
}
if (jinwei != 0){
ret.digit[ret.size++] = jinwei;
}
return ret;
}
}a,b,c;
char str1[1000], str2[1000];
int main(){
while (scanf("%s%s", str1, str2) != EOF){
a.set(str1);
b.set(str2);
c = a + b;
c.output();
}
return 0;
}
下面讨论高精度乘法,这里主要讨论高精度整数乘以一般小整数的运算。
- 例4.12 N 的阶乘
输入的数据可能不大,但是输出的结果可能非常大
#include<stdio.h>
#include<string.h>
struct bigInteger
{
int digit[1000];
int size;
void init(){
for (int i=0;i<1000;i++){
digit[i] = 0;
}
size = 0;
}
void set(int x){
init();
do{
digit[size++] = x % 10000;
x /= 10000;
}while(x!=0);
}
void output(){
for (int i=size-1;i>=0;i--){
if (i!=size-1)
printf("%04d", digit[i]);
else
printf("%d", digit[i]);
}
printf("\n");
}
bigInteger operator * (int x) const{
bigInteger ret;
ret.init();
int jinwei = 0;
for (int i=0;i<size;i++){
int tmp = x * digit[i] +jinwei;
jinwei = tmp / 10000;
tmp %= 10000;
ret.digit[ret.size++] = tmp;
}
if (jinwei != 0){
ret.digit[ret.size++] = jinwei;
}
return ret;
}
}a;
int main(){
int n;
while (scanf("%d", &n) != EOF){
a.init();
// a.set(1);
a.digit[0] = 1;
a.size = 1;
for (int i=1;i<=n;i++){
a = a * i;
}
a.output();
}
return 0;
}
- 例4.13 进制转换
大数的进制转化,将 M 进制的整数 x 转换为 N 进制的数,这其中涉及到高精度整数与普通整数的求积,高精度整数之间的求和,高精度整数除以普通整数,高精度整数对普通整数求模等。
#include<stdio.h>
#include<string.h>
#define maxDigits 100
struct bigInteger{
int digits[maxDigits];
int size;
void init(){
for (int i=0;i<maxDigits;i++){
digits[i] = 0;
}
size = 0;
}
void set(int x){
init();
do{
digits[size++] = x % 10000;
x /= 10000;
}while(x!=0);
}
void output(){
for (int i=size-1;i>=0;i--){
if (i!=size-1)
printf("%04d",digits[i]);
else
printf("%d", digits[i]);
}
printf("\n");
}
bigInteger operator * (int x) const{
bigInteger ret;
ret.init();
int jinwei = 0;
for (int i=0;i<size;i++){
int tmp = x*digits[i] + jinwei;
jinwei = tmp / 10000;
tmp %= 10000;
ret.digits[ret.size++] = tmp;
}
if (jinwei!=0)
ret.digits[ret.size++] = jinwei;
return ret;
}
bigInteger operator + (const bigInteger &A) const{
bigInteger ret;
ret.init();
int jinwei = 0;
for (int i=0;i<A.size || i<size;i++){
int tmp = A.digits[i] + digits[i] + jinwei;
jinwei = tmp / 10000;
tmp %= 10000;
ret.digits[ret.size++] = tmp;
}
if (jinwei!=0)
ret.digits[ret.size++] = jinwei;
return ret;
}
bigInteger operator / (int x) const {
bigInteger ret;
ret.init();
int yushu = 0;
for (int i=size-1;i>=0;i--){
int t = (yushu * 10000 + digits[i]) / x;
int r = (yushu * 10000 + digits[i]) % x;
ret.digits[i] = t;
yushu = r;
}
ret.size = 0;
for (int i=0;i<maxDigits;i++){
if (digits[i]!=0)
ret.size = i;
}
ret.size++;
return ret;
}
int operator % (int x) const {
int yushu = 0;
for (int i=size-1;i>=0;i--){
int t = (yushu * 10000 + digits[i]) / x;
int r = (yushu * 10000 + digits[i]) % x;
yushu = r;
}
return yushu;
}
}a,b,c;
char str[10000];
char ans[10000];
int main(){
int n,m;
while (scanf("%d%d", &m, &n) != EOF){
scanf("%s", str);
int L = strlen(str);
a.set(0); //用来保存转换成 10 进制的 m 进制数
b.set(1); // 转换过程中每一位的权重
for (int i=L-1;i>=0;i--){
int t;
if (str[i] >= '0' && str[i] <= '9')
t = str[i] - '0';
else
t = str[i] - 'A' + 10;
a = a + b * t;
b = b * m;
// b.output();
}
int size = 0;
do{
//将10进制转换成 n 进制
int t = a % n;
if (t>=10)
ans[size++] = t - 10 + 'a';
else
ans[size++] = t + '0';
a = a / n;
// a.output();
}while(a.digits[0] != 0 || a.size != 1);
for (int i=size-1;i>=0;i--){
printf("%c", ans[i]);
}
printf("\n");
}
return 0;
}