题目描述:
输入两个正整数 x0,y0,求出满足下列条件的 P,Q 的个数:
-
P,Q 是正整数。
-
要求P,Q 以 x0 为最大公约数,以 y0 为最小公倍数。
试求:满足条件的所有可能的 P,Q 的个数。
输入格式
一行两个正整数 x0,y0。
输出格式
一行一个数,表示求出满足条件的 P,Q 的个数。
输入输出样例
输入 #1
3 60
输出 #1
4
说明/提示
P,Q 有 4 种:
- 3,60。
- 15,12。
- 12,15。
- 60,3。
对于 100% 的数据,2≤x0,y0≤10^5。
【题目来源】
P1029 [NOIP2001 普及组] 最大公约数和最小公倍数问题
思路及部分代码:
1. 求出最大最小
//返回最大值或者最小值
//根据 d 的值返回最大或者最小
int max_min(int a, int b,int d){
int max, min;
if(a >= b){
max = a;
min = b;
}
else{
max = b;
min = a;
}
if(d == 0) return min;
else return max;
}
2. 欧几里得算法求最大公约数
原本想遍历出结果,但时间复杂度太高,所以学习了一下欧几里得算法。
欧几里得算法(辗转相除法):其基本思想是用较大的数除以较小的数,然后用较小的数去除余数,再用新的余数去除小的数,如此反复,直到余数为零为止,此时除数就是最大公约数。
//求出最大公约数
/*
欧几里得算法(辗转相除法),其基本思想是用较大的数除以较小的数,
然后用较小的数去除余数,再用新的余数去除小的数,如此反复,
直到余数为零为止,此时除数就是最大公约数。具体步骤如下:
*/
int Common_divisor_max(int x, int y){
int max = max_min(x,y,1); //求出最大数
int c_n = x+y-max; //除数
int c = max % c_n; //被除数
while(c != 0){
int di = c; //除数被除数交换
c = c_n;
c_n = di;
c = c % c_n;
}
return c_n;
/*
for(int i = max;i >= 1;i--){
if(x%i == 0 && y%i == 0){ //满足最大公约数
return i;
}
}
*/
//return 0;
}
3. 最小公倍数
也是考虑到时间复杂度问题,把遍历改成利用公约数求公倍数。
关系:两个数的乘积等于它们的最大公约数与最小公倍数的乘积。
//求出最小公倍数
int Common_multiple_min(int x,int y,int yue){
return x*y/yue;
/*
int max = max_min(x,y,1); //求出最大数
for(int i = max; i<= x*y;i = i + yue){
if(i%x == 0 && i%y == 0){
return i;
}
}
*/
//return -1; //错误
}
4. 思路遍历
a. 我们知道在P/Q是在输入的x,y中,所以这里给出了一个范围。
b. 但在实际情况下我们所P/Q的遍历有太多的无用值,浪费我们的计算时间。
c. 我们由最小公倍数中的关系可得一个判定方式。
d. 再在我们的函数中,可以根据最大公约数与X再进行一次判定,最后再与Y判定。
P = max_min(x,y, 0);
Q = max_min(x,y, 1);
int cnt = 0; //计数满足要求的Q P
for(int i = P; i <= Q; i = i + x){
//遍历方式求解,时间复杂度太高
for(int j = Q; j >= P; j= j - x){
if(j*i == x*y){
int yue = Common_divisor_max(i, j);
if(yue != x) ;//跳过
else if(Common_multiple_min(i,j, yue) == y){
cnt++;
//printf("P = %d Q = %d \r\n", i,j);
}
}
}
}
总代码:
#include <stdio.h>
//返回最大值或者最小值
int max_min(int a, int b,int d){
int max, min;
if(a >= b){
max = a;
min = b;
}
else{
max = b;
min = a;
}
if(d == 0) return min;
else return max;
}
//求出最大公约数
/*
欧几里得算法(辗转相除法),其基本思想是用较大的数除以较小的数,
然后用较小的数去除余数,再用新的余数去除小的数,如此反复,
直到余数为零为止,此时除数就是最大公约数。具体步骤如下:
*/
int Common_divisor_max(int x, int y){
int max = max_min(x,y,1); //求出最大数
int c_n = x+y-max; //除数
int c = max % c_n; //被除数
while(c != 0){
int di = c; //除数被除数交换
c = c_n;
c_n = di;
c = c % c_n;
}
return c_n;
/*
for(int i = max;i >= 1;i--){
if(x%i == 0 && y%i == 0){ //满足最大公约数
return i;
}
}
*/
//return 0;
}
//求出最小公倍数
int Common_multiple_min(int x,int y,int yue){
return x*y/yue;
/*
int max = max_min(x,y,1); //求出最大数
for(int i = max; i<= x*y;i = i + yue){
if(i%x == 0 && i%y == 0){
return i;
}
}
*/
//return -1; //错误
}
int main (){
int x,y;
scanf("%d %d",&x, &y);
int P,Q;
P = max_min(x,y, 0);
Q = max_min(x,y, 1);
int cnt = 0; //计数满足要求的Q P
for(int i = P; i <= Q; i = i + x){
//遍历方式求解,时间复杂度太高
for(int j = Q; j >= P; j= j - x){
if(j*i == x*y){
int yue = Common_divisor_max(i, j);
if(yue != x) ;//跳过
else if(Common_multiple_min(i,j, yue) == y){
cnt++;
//printf("P = %d Q = %d \r\n", i,j);
}
}
}
}
printf("%d\r\n",cnt);
return 0;
}
总结:
这个程序的主要功能是寻找满足条件的数对 P 和 Q,使得它们的乘积等于输入的 x 和 y 的乘积。程序的思路是在 P 和 Q 的所有可能组合中进行遍历,找到满足条件的数对并计数。
不足之处:
-
时间复杂度高:程序的时间复杂度较高,主要是由于双层嵌套的循环造成的。这使得程序在处理大量数据时的计算成本较高,需要较长的运行时间。
-
重复计算:程序在遍历所有可能的 P 和 Q 组合时,没有避免重复计算。这意味着某些计算可能会被多次执行,浪费了计算资源。
-
输出不必要信息:程序在找到满足条件的数对 P 和 Q 时,会输出它们的值。这可能会导致输出大量不必要的信息,影响程序的可读性。
改进意见:
-
优化算法:可以通过数学推导和优化算法来降低程序的时间复杂度。例如,可以使用辗转相除法来计算最大公约数,使用最小公倍数的性质来避免重复计算等。
-
避免重复计算:可以使用缓存等技术来避免重复计算。例如,可以将已经计算过的结果存储在一个哈希表中,避免重复执行相同的计算。
-
简化输出:可以通过修改程序的输出方式来简化输出内容。例如,可以只输出满足条件的数对的数量,而不是输出每个满足条件的数对的值。