1. PAT B1019/A1069数字黑洞问题
代码为,需要注意的是数组定义大小为4但是在写sort函数时可以使用sort(a,a+4,cmp)这种表达方式,%04d可以补足4位,不足的用0补全:
#include<cstdio>
#include<algorithm>
using namespace std;
bool cmp(int a, int b)
{
return a > b; //递减排序
}
//注意这种写法实现了对数组的实际改变,传入的应当相当于一个地址
void to_arr(int n, int num[])//从低位开始存储到数组当中
{
for(int i = 0; i < 4; i++)
{
num[i] = n % 10;
n /= 10;
}
}
int to_number(int num[])
{
int sum = 0;
for(int i = 0; i < 4; i++)
{
sum = sum * 10 + num[i];
}
return sum;
}
int main()
{
int n, MAX, MIN;
scanf("%d", &n);
int num[4];//注意这里将5改为4,因为num+4是最后一位的下一位
while(1)
{
to_arr(n, num);
sort(num, num + 4, cmp);
MAX= to_number(num);
sort(num, num + 4);
MIN = to_number(num);
n = MAX - MIN;
printf("%04d - %04d = %04d\n", MAX, MIN, n);//实现当不足四位数时补0的操作:0123
if(n == 0 || n == 6174)
break;
}
return 0;
}
2.辗转相除法求最大公约数
#include<cstdio>
#include<algorithm>
using namespace std;
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 / gcd(a, b)
代码为:
#include<cstdio>
#include<algorithm>
using namespace std;
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", a * b / gcd(a,b));
}
return 0;
}
运行结果:
3. 判断是否为素数,注意<math.h>中定义的sqrt中的参数必须是double型的,所以最好加上1.0*n,当然直接用n也不会出错,<= sqr,c++中允许使用true和false者两种bool类型的:
#include<cstdio>
#include<math.h>
#include<algorithm>
using namespace std;
bool isPrime(int n)
{
if(n <= 1)
return false;
int sqr = (int)sqrt(1.0 * n);
for(int i = 2; i <= sqr; i++)
{
if(n % i == 0)
return false;
}
return true;
}
int main()
{
int m;
while(scanf("%d", &m) != EOF)
{
if(isPrime(m))
printf("%d is a prime!\n", m);
else
printf("%d is not a prime!\n", m);
}
return 0;
}
更简单的判断一段区间内的所有素数的方法,即从2开始,1不是素数,为2的倍数一定不是素数,3不是2的倍数(没有因为是前面的素数的倍数而被筛掉),一定是素数,按照这种思想依次类推,即可得到最终的结果:
#include<cstdio>
#include<math.h>
#include<algorithm>
using namespace std;
const int maxn = 101;
int prime[maxn], pNum = 0;//按照顺序存储素数
bool p[maxn] = {false};//false表示为素数
void find_prime()
{
for(int i = 2; i < maxn; i++)
{
if(p[i] == false)
{
prime[pNum++] = i;
for(int j = i + i; j < maxn; j += i)
{
p[j] = true;
}
}
}
}
int main()
{
find_prime();
for(int i = 0; i < pNum; i++)
printf("%d ", prime[i]);
printf("\n");
return 0;
}
结果:
B1013整素数问题
输出从2开始的第m到第n个素数,第m个素数时prime[m-1],要注意对maxn进行修改,过小时无法通过:
#include<cstdio>
#include<math.h>
#include<algorithm>
using namespace std;
int M,N;
const int maxn = 1000001;//对maxn进行修改
int prime[maxn], pNum = 0;//按照顺序存储素数
bool p[maxn] = {false};//false表示为素数
void find_prime()
{
for(int i = 2; i < maxn; i++)
{
if(p[i] == false)
{
prime[pNum++] = i;
if(pNum >= N)
break;
for(int j = i + i; j < maxn; j += i)
{
p[j] = true;
}
}
}
}
int main()
{
int count = 0;
scanf("%d%d", &M, &N);
find_prime();
for(int i = M - 1; i <= N - 1; i++)
{
printf("%d", prime[i]);
count++;
if(count % 10 != 0 && i < N - 1)
printf(" ");
else
printf("\n");//当prime[i]的i为N-1时,要输出换行符
}
return 0;
}
结果:
4.质因子分解问题
质因子就是素数,所以还要用到前面的素数表,一个数的因子只可能有一个大于sqrt(n)的因子,所以或者所有因子都小于sqrt(n)或者有1个是大于sqrt(n)的,而有1个大于sqrt(n)的质因子,如14 = 2 * 7,17 = 17, 1不是素数,所以也不是质因子,不用在表达式中写出,按照这个思路可以得到各个质因子以及相同质因子的个数,n%prime[i],n/=prime[i]循环直到n%prime[i]不为0,代码为:
#include<cstdio>
#include<math.h>
#include<algorithm>
using namespace std;
const int maxn = 1000001;//对maxn进行修改
int prime[maxn], pNum = 0;//按照顺序存储素数
bool p[maxn] = {false};//false表示为素数
struct factor{
int x;//具体的质因子
int cnt = 0;//同一质因子的个数
}fac[10];
//可以获得全部的素数表prime[n]
void find_prime()
{
for(int i = 2; i < maxn; i++)
{
if(p[i] == false)
{
prime[pNum++] = i;
for(int j = i + i; j < maxn; j += i)
{
p[j] = true;
}
}
}
}
int main()
{
find_prime();
int n, num = 0;//num是不同质因子的个数
scanf("%d", &n);
int N = n;
int sqr = (int)sqrt(1.0 * n);
if(n == 1)
printf("1=1");//不用换行
else
{
for(int i = 0; i < pNum && prime[i] <= sqr; i++)
{
if(n % prime[i] == 0)
{
fac[num].x = prime[i];
//fac[num].cnt = 0;
while(n % prime[i] == 0)
{
fac[num].cnt++;
n /= prime[i];
}
num++;
}
if(n == 1)
break;//1已经没有继续进行运算的必要了
}
if(n != 1)//说明有且有1个大于sqr的质因子,即为当前的n,可能为N自身,N为素数N=N,num=1
{
fac[num].x = n;
fac[num].cnt = 1;
num++;
}
printf("%d=", N);
for(int i = 0; i < num; i++)
{
if(i > 0 )
printf("*");
printf("%d", fac[i].x);
if(fac[i].cnt > 1)
printf("^%d", fac[i].cnt);
}
}
return 0;
}
运行结果:
5.大整数运算问题
对于一个结构体,不能在定义时初始化,即不可以struct ss{ int x = 0; int y = 0};这种形式,因为结构体在定义时是没有分配空间的,必须在声明时实例化,或者通过构造函数实现初始化!!!
加法:
#include<cstdio>
#include<string.h>//包括了memset
#include<math.h>
#include<algorithm>
using namespace std;
struct bign{
int digit[1000];
int len;
bign(){
memset(digit, 0, sizeof(digit));
len = 0;
}
};
bign change(char str[])
{
bign a;
a.len = strlen(str);
for(int i = 0; i < a.len; i++)
a.digit[i] = str[a.len - 1 - i] - '0';
return a;
}
bign add(bign a, bign b)
{
bign c;
int carry = 0;
for(int i = 0; i < a.len || i < b.len; i++)//以最长的为界限
{
int temp = a.digit[i] + b.digit[i] + carry;
c.digit[c.len++] = temp % 10;
carry = temp / 10;//可能为0或者1,2等
}
//最高位,长度为len,但是len-1是最高位的下标
if(carry != 0)
{
c.digit[c.len++] = carry;
}
return c;
}
void print(bign c)
{
for(int i = c.len - 1; i >= 0; i--)
{
printf("%d", c.digit[i]);
}
}
int main()
{
char str1[1000], str2[1000];
scanf("%s%s", str1, str2);
bign a = change(str1);
printf("%d\n", a.len);
bign b = change(str2);
printf("%d\n", b.len);
bign c = add(a,b);
printf("%d\n", c.len);
print(c);
return 0;
}
运行结果:
减法,减法需要在低位不够时向高位借1,也是从低位开始运算,其它部分与上面的完全一致,只是sub函数不同,注意这个函数只能解决大的减去小的情况,如果小的减去大的,要先判断出大小,再进行大的减去小的并取负:
#include<cstdio>
#include<string.h>//包括了memset
#include<math.h>
#include<algorithm>
using namespace std;
struct bign{
int digit[1000];
int len;
bign(){
memset(digit, 0, sizeof(digit));
len = 0;
}
};
bign change(char str[])
{
bign a;
a.len = strlen(str);
for(int i = 0; i < a.len; i++)
a.digit[i] = str[a.len - 1 - i] - '0';
return a;
}
bign sub(bign a, bign b)
{
bign c;
for(int i = 0; i < a.len || i < b.len; i++)
{
if(a.digit[i] < b.digit[i])
{
a.digit[i + 1]--;
a.digit[i] += 10;
}
c.digit[c.len++] = a.digit[i] - b.digit[i];
}
//c.len - 1 == 0时只有1位,c.len == 1,c.len - 1是最高位的下标
while(c.len - 1 >= 1 && c.digit[c.len -1] == 0)
{
c.len--;
}
return c;
}
void print(bign c)
{
for(int i = c.len - 1; i >= 0; i--)
{
printf("%d", c.digit[i]);
}
}
int main()
{
char str1[1000], str2[1000];
scanf("%s%s", str1, str2);
bign a = change(str1);
printf("%d\n", a.len);
bign b = change(str2);
printf("%d\n", b.len);
bign c = sub(a,b);
printf("%d\n", c.len);
print(c);
return 0;
}
运行结果:
乘法,与加法比较类似,主要注意进位和最高位的问题,与一般乘法思路不同的是处理大整数和小整数的问题中,是用大整数的每一位去乘完整的小整数:
#include<cstdio>
#include<string.h>//包括了memset
#include<math.h>
#include<algorithm>
using namespace std;
struct bign{
int digit[1000];
int len;
bign(){
memset(digit, 0, sizeof(digit));
len = 0;
}
};
bign change(char str[])
{
bign a;
a.len = strlen(str);
for(int i = 0; i < a.len; i++)
a.digit[i] = str[a.len - 1 - i] - '0';
return a;
}
bign mul(bign a, int b)
{
bign c;
int carry = 0;
for(int i = 0; i < a.len; i++)
{
int temp = a.digit[i] * b + carry;
c.digit[c.len++] = temp % 10;
carry = temp / 10;
}
//与加法不同,乘法的进位可能会有多位,大于10,加法不能进位10
while(carry != 0)
{
c.digit[c.len++] = carry % 10;
carry /= 10;
}
return c;
}
void print(bign c)
{
for(int i = c.len - 1; i >= 0; i--)
{
printf("%d", c.digit[i]);
}
}
int main()
{
char str1[1000];
int b;
//scanf以空格作为结束
scanf("%s%d", str1, &b);
bign a = change(str1);
printf("a.len = %d\n", a.len);
bign c = mul(a, b);
printf("mul(a,b) = ");
print(c);
return 0;
}
运行结果:
除法:除法比较特别,从高位开始计算,每次的余数*10加上下一位作为下一位的被除数:
#include<cstdio>
#include<string.h>//包括了memset
#include<math.h>
#include<algorithm>
using namespace std;
int r = 0;
struct bign{
int digit[1000];
int len;
bign(){
memset(digit, 0, sizeof(digit));
len = 0;
}
};
bign change(char str[])
{
bign a;
a.len = strlen(str);
for(int i = 0; i < a.len; i++)
a.digit[i] = str[a.len - 1 - i] - '0';
return a;
}
bign divide(bign a, int b)
{
bign c;
c.len = a.len;//使得商与被除数的位数一样长
for(int i = c.len - 1; i >= 0; i--)
{
r = r * 10 + a.digit[i];
if(r < b)
c.digit[i] = 0; //这里实际上应该可以直接用r/b如果r<b时,r/b=0
else
{
c.digit[i] = r / b;
r = r % b;
}
}
while(c.len - 1 >= 1 && c.digit[c.len - 1] == 0)
{
c.len--;
}
return c;
}
void print(bign c)
{
for(int i = c.len - 1; i >= 0; i--)
{
printf("%d", c.digit[i]);
}
}
int main()
{
char str1[1000];
int b;
//scanf以空格作为结束
scanf("%s%d", str1, &b);
bign a = change(str1);
printf("a.len = %d\n", a.len);
bign c = divide(a, b);
printf("divide(a,b) = ");
print(c);
return 0;
}
运行结果:
借此解决一下王道上给出的问题N的阶乘,N<=1000,每个数都可以作为小整数,而每次的结果都可以作为大整数,先将"1"转换为bign,依次为基础逐渐与各个整数相乘可以得到结果,代码为,只是将主函数上做了改变,其它的地方都未作出改变:
#include<cstdio>
#include<string.h>
#include<math.h>
#include<algorithm>
using namespace std;
struct bign{
int digit[1000];
int len;
bign(){
memset(digit, 0, sizeof(digit));
len = 0;
}
};
bign change(char str[])
{
bign a;
a.len = strlen(str);
for(int i = 0; i < a.len; i++)
{
a.digit[i] = str[a.len - 1 - i] - '0';
}
return a;
}
bign mul(bign a, int b)
{
bign c;
int carry = 0;
for(int i = 0; i < a.len; i++)
{
int temp = a.digit[i] * b + carry;
c.digit[c.len++] = temp % 10;
carry = temp / 10;
}
while(carry != 0)
{
c.digit[c.len++] = carry % 10;
carry /= 10;
}
return c;
}
void print(bign c)
{
for(int i = c.len - 1; i >= 0; i--)
{
printf("%d", c.digit[i]);
}
}
int main()
{
int N;
char str[100] = "1";
while(scanf("%d", &N) != EOF)
{
bign res = change(str);
for(int i = 2; i <= N; i++)
{
res = mul(res, i);
}
print(res);
}
return 0;
}
运行结果: