一、问题说明
基础:
求N个数的最大公约数和最大公倍数。
提高:
Hanks博士是BT(Bio-Tech,生物技术)领域的知名专家,他的儿子名叫Hankson。现在,刚刚放学回家的Hankson正在思考一个有趣的问题。
今天在课堂上,老师讲解了如何求两个正整数c1和c2的最大公约数和最小公倍数。现在Hankson认为自己已经熟练地掌握了这些知识,他开始思考一个“求公约数”和“求公倍数”之类问题的“逆问题”,这个问题是这样的:已知正整数a0,a1,b0,b1,设某未知正整数x满足:
1、 x和a0的最大公约数是a1;
2、 x和b0的最小公倍数是b1。
Hankson的“逆问题”就是求出满足条件的正整数x。但稍加思索之后,他发现这样的x并不唯一,甚至可能不存在。因此他转而开始考虑如何求解满足条件的x的个数。请你帮助他编程求解这个问题。
输入格式
输入第一行为一个正整数n,表示有n组输入数据。接下来的n行每行一组输入数据,为四个正整数a0,a1,b0,b1,每两个整数之间用一个空格隔开。输入数据保证a0能被a1整除,b1能被b0整除。
输出格式
输出共n行。每组输入数据的输出结果占一行,为一个整数。
对于每组数据:若不存在这样的x,请输出0;若存在这样的x,请输出满足条件的x的个数;
样例输入
2
41 1 96 288
95 1 37 1776
样例输出
6
2
二、分析
1、基础
用一数组a[]存储输入的N个数,先用gcd()求两个数的最大公约数,然后求a[1]=gcd(a[0],a[1]),再求a[2]=gcd(a[1],a[2]),依次下去,最后a[N-1]既是结果。最小公倍数则调用lcm()。
2、提高
已知
gcd(x,a0)=a1……①
lcm(x,b0)=b1;
根据两个数的最大公约数和最小公倍数的关系,mn/gcd(m,n)=lcm(m,n);
转化得,gcd(x,b0)=xb0/b1……②
由①②根据最大公约数的性质得,
gcd(x/a1,a0/a1)=1……③
gcd(b1/b0,b1/x)=1……④
符合条件的x必然满足③式和④式,故枚举b1的因子且满足③、④式就是x。
算法流程图
三、运行结果
1、基础
2、提高
四、程序实现
1、N个数的最大公约数
* 名称:第二次作业
* 文件名:基础.cpp
* 说明:多个数的最大公约数及最小公倍数
*/
#include"iostream"
using namespace std;
//求最大公约数
int gcd(int a,int b)
{
int ans = 1;//储存第一步中约掉的若干个2
int gcd;//储存最终返回的结果
while (a % 2 == 0 && b % 2 == 0)//如果a,b均为偶数则用2约简
{
a /= 2;
b /= 2;
ans *= 2;
}
while (a != b)//判断两数是否相等,相等则得出最大约数
{
if (a > b)
a -= b;//以较大的数减较小的数
else
b -= a;//以较大的数减较小的数
}
gcd = a * ans; //求第一步中约掉的若干个2与第二步中等数的乘积
return gcd;
}
//求最小公倍数
int lcm(int a, int b) {
return(a*b / gcd(a, b));
}
int main() {
int a[1000];
int temp1, temp2;
int i = 0;
cin >> i;//输入需要几个数
for (int j = 0; j < i; j++) {
cin >> a[j];
}
for (int k = 0; k < i - 1; k++) {
temp1 = a[0];
temp1 = gcd(temp1, a[k + 1]);
}
for (int k = 0; k < i - 1; k++) {
temp2 = a[0];
temp2 = lcm(temp2, a[k + 1]);
}
int gcd = temp1;
cout << "最大公约数 " << temp1 << endl;
int lcm = temp2;
cout << "最小公倍数 " << temp2 << endl;
system("pause");
}
2、Hankson问题
* 名 称:第二次作业
* 文件名:homework2.cpp
* 说 明:Hankson问题实现
*/
#include"iostream"
using namespace std;
//求最大公约数
int gcd(int a, int b)
{
int ans = 1;//储存第一步中约掉的若干个2
int gcd;//储存最终返回的结果
while (a % 2 == 0 && b % 2 == 0)//如果a,b均为偶数则用2约简
{
a /= 2;
b /= 2;
ans *= 2;
}
while (a != b)//判断两数是否相等,相等则得出最大约数
{
if (a > b)
a -= b;//以较大的数减较小的数
else
b -= a;//以较大的数减较小的数
}
gcd = a * ans; //求第一步中约掉的若干个2与第二步中等数的乘积
return gcd;
}
int main() {
int n;//n组数据
cin >> n;
int a0, a1, b0, b1;//正整数a0,a1,b0,b1
for (int i = 1; i <= n;i++) {
int num = 0;
cin >> a0 >> a1 >> b0 >> b1;
// 枚举b1的因子,判定条件为j*j<=b1,防止重复记录同一个因子
for (int j = 1; j*j <= b1; j++) {
if (b1%j == 0) {
int x = j;
if (x%a1 == 0)
//判断最大公约数是否为1
if (gcd(x / a1, a0 / a1) == 1 && gcd(b1 / b0, b1 / x) == 1)
num++;
x = b1 / x;
if(x != j && x % a1 == 0)
if (gcd(x / a1, a0 / a1) == 1 && gcd(b1 / b0, b1 / x) == 1)
num++;
}
}
cout << num << endl;
}
system("pause");
return 0;
}
五、个人总结
数学的学习与算法的设计是紧密结合的,本次作业中核心的就在于对最大公约数的数学知识的充分了解,并且结合计算机的优势,可以不断循环计算。两者结合能够很有效的解决一些问题。