题目链接
校OJ每日一题
文章目录
问题重述
题目描述
输入二个正整数x0,y0(2<=x0<100000,2<=y0<=1000000),求出满足下列条件的P,Q的个数。
条件:
-
P,Q是正整数;
-
要求P,Q以x0为最大公约数,以y0为最小公倍数。
试求:
满足条件的所有可能的两个正整数的个数。
输入格式
每个测试文件只包含一组测试数据,每组两个正整数x0和y0(2<=x0<100000,2<=y0<=1000000)。
输出格式
对于每组输入数据,输出满足条件的所有可能的两个正整数的个数。
下面是对样例数据的说明:
输入3 60
此时的P Q分别为:
3 60
15 12
12 15
60 3
所以,满足条件的所有可能的两个正整数的个数共4种。
输入输出样例
#样例1
输入:3 60
输出:4
说明/提示
无
大概思路
在做这题之前,我们需要知道什么是最大公约数,什么是最小公倍数?
最大公约数
定义
又叫最大公因数,即两个或两个以上的数共有的最大的那个因数就是最大公因数。
比如12和20,它们有共同的因子1, 2, 4,其中4就是他们的最大公因数;
比如20和40,它们有共同的因子1,2, 4, 5, 10, 20,其中20就是他们的最大公因数
求法1:暴力法
根据最大公因数的定义以及上面的例子,我们可以知道,最大公因数不会大于这n个数中的最小的那个数,所以我们可以从 m i n ( x i , i = 1 , 2 , 3 , . . . . n ) min(x_i, i = 1, 2, 3, ....n) min(xi,i=1,2,3,....n)开始查找,遍历这n个数,用这个数分别去除这n个数,如果都能被整除,那么这个数就是最大公因数,否者这个数就减一,实现代码如下
a = list(map(int, input().split())) # 输入一组数据
number = min(a) # 找到这堆数据中最小的数
while number >= 1: # 1肯定能整除这一堆数
for i in a: # 遍历数组
if i % number: # 如果不能被整除,那么就检查下一个数
number -= 1
break
else: # 循环正常退出的话就说明这个数是最大公因数
print("最大公因数是:", number)
break
求法2:更相减损法
更相减损法,也叫更相减损术,出自《九章算术》中的一种求最大公约数的算法,它只能够求两个数中的最大公因数,具体实现就是不断的用两个数中的较大的那个数减去较小的那个数,然后再用他们的差与减数(就是之前较小的那个数)比较,再用较大的数减去较小的那个数,知道他们的差与减数相等的时候,这个差就是这两个数的最大公因数,具体代码实现如下:
number1, number2 = map(int, input().split()) # 输入两个数
def fun2(number1, number2):
while number1 != number2:
if number1 > number2:
number1 -= number2
else:
number2 -= number1
return number1
print(number1, number2,"的最大公因数是:",fun2(number1, number2))
求法3:辗转相除法
辗转相除法与更相减损术类似,都是可以很快求出两个数的最大公因数,具体步骤如下:
1、找到较大的那个数,用较大的数除以较小的那个数,当他们的商不为0的时候,记录余数的值,转2,否则转3
2、原来的除数变被除数,余数变除数,转1
3、除数就是这两个数的最大公因数了
具体代码实现如下:
number1, number2 = map(int, input().split())
def fun3(number1, number2):
a = max(number1, number2)
b = min(number1, number2)
while a % b:
b, a = a % b, b
return b
print(number1, number2,"的最大公因数是:", fun3(number1, number2))
最小公倍数
定义
两个或两个以上的数共有的倍数就是它们的公倍数,其中除0以外的最小的那个倍数就是他们的最小公倍数
例如
18和20的最小公倍数是180
1和14的最小公倍数是14
2和4的最小公倍数是4
求法1:暴力法
根据最小公倍数的定义以及上面举的例子,我们能清楚的知道最小公倍数一定是大于或等于这一些数中最大的那一个数,所以我们就可以从最大的那一个数开始,遍历这一堆数,判断是否每一个数字都能整除这一个数,具体代码实现如下
a = list(map(int, input().split()))
def fun4(a):
mmax = max(a)
while True:
for i in a: # 遍历整个数组判断是否每一个数字都能整除假定的这个最小公倍数
if mmax % i: # 如果有一个数不能整除假定的这个最小公倍数,说明假定的这个数不对
mmax += 1
break
else: # 循环正常退出说明假定的这个数就是最小公倍数
return mmax
print("最小公倍数为:", fun4(a))
暴力法的缺点众所周知就是当数据很大的时候耗费的时间可能会很长,所以我们介绍一个比较快捷的算法,公式法
求法2:公式法
公式法的定义:由于两个数的乘积等于他们的最大公约数和最小公倍数的乘积,所以我们求出了这两个数的最大公因数也就可以求出这两个数的最小公倍数了。具体代码实现如下
def fun3(number1, number2):
a = max(number1, number2)
b = min(number1, number2)
while a % b:
b, a = a % b, b
return b
def fun5(number1, number2):
return number1 * number2 // fun3(number1, number2)
number1, number2 = map(int, input().split())
print(number1, number2, "的最小公倍数是:", fun5(number1, number2))
实现过程
再了解完最小公倍数和最大公因数之后,我们就可以开始着手做这一题了,我们经过观察题目所给的例子,发现P,Q一定在x0和y0之间,这是因为x0,y0分别作为最大公因数和最小公倍数的限制,所以我们只需要遍历x0和y0之间的每一个数就行,不过再看数据范围,稍微有点大,那有没有哪儿可以优化呢,我们知道,因数,因数,而且还是最小公因数,那我们就可以控制每次的增量都是这个因数,这样是不是就能少去判断一部分的数字并且能保证不会漏判一组数据。
不过即使像上面那样也还是不够的,我们再进一步想,能不能只用一层循环就能将P, Q两个数给找出来,那必须是可以的,由公式法我们可以知道,当我们假设P已知,那么
Q
=
x
0
×
y
0
P
Q=\frac{x_0\times y_0}{P}
Q=Px0×y0,这样我们就可以用一层循环找到遍历每一组P和Q
最后,再找到P,Q之后,我们能保证它们的最小公倍数是
y
0
y_0
y0,但是并不能保证最大公因数是
x
0
x_0
x0,所以我们还需要再检查一次每组数据的最大公因数是不是
x
0
x_0
x0,具体代码实现如下
完整AC代码
def judge(p, q): # 判断两个数的最大公因数是否是x
while p != q:
if p > q:
p -= q
else:
q -= p
if p == x:
return True
return False
x, y = map(int, input().split())
cnt = 0
p = x
a = x * y
while p <= y:
if a % p == 0:
q = a // p
if judge(p, q):
cnt += 1
p += x
print(cnt)