一、题目名称:
Hankson 的趣味题
二、题目内容:
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的个数。
三、算法设计:
由最大公约数和最小公倍数的关系可知①式:
由已知②③式:
式中a0,a1,b0,b1均为用户自己输入,故可以视为已知。
由②③式联立,运用性质①经过变形可得式④:
联立以上①②③④各式并整理可得式⑤:
由此,可以初步得到x的条件:既满足公式②又满足公式⑤,只需将x从1开始枚举到b1即可。
但是,由上次的作业可以知道,运用辗转相除法计算最大公约数时,运行的时间会随数据规模的增大而逐渐增加。因此对该算法进行优化。
由最大公约数的性质得⑥式:
因此可以将⑤式化简为⑦:
也可以将②式化简为⑧:
这样就将求最大公约数时的数据进一步缩小。同时由③式可知,x必为b1的因子,因此只需将枚举范围缩小至1到sqrt(b1)即可,但应该注意,当x为b1的因子时,b1/x也为b1的因子。
对于求最大公约数,采用辗转相除法即可。
四、调试截图:
-
输入数据调试
-
计数器变化调试
五、测试结果:
六、遇到的困难及解决方案:
本次题目中共有5个变量,但其中4个是由输入得到,只有一个待求,不难让人想到用枚举法暴力**。但是在真正的比赛环境中,往往会有时间限制,所以暴力法只能解决部分问题,而不能拿到满分。因此必须在暴力枚举的基础上进行优化。而这其中遇到的最大的困难应该是数学上的证明,通过找gcd和lcm之间的关系和其各自的性质,然后结合题目条件加以推导,尽可能的缩小传入gcd()函数的两个参数的值,容易找到一些判断x的优化方法,这使得程序的运行效率得到很大的提升。当然,数学公式的编辑也较为繁琐,上文采用的是在线公式编辑网站,格式为png格式的图片格式。
七、源代码
/**
* All rights Reserved, Designed By YanCX
* @Title: Hankson.java
* @Package chaptertwo
* @Description: TODO
* @author: YanCX
* @date: 2019年3月23日 下午6:47:55
* @version V1.0
* @Copyright: 2019 All rights reserved.
*/
package chaptertwo;
import java.util.Scanner;
/**
* @ClassName: Hankson
* @Description:
* @author: YanCX
* @date: 2019年3月23日 下午6:47:55
*/
public class Hankson {
/**
* @Title: main
* @Description: TODO(这里用一句话描述这个方法的作用)
* @param: @param args
* @return: void
* @throws
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
int n = 0; // 次数
Scanner sc = new Scanner(System.in);
n = sc.nextInt();
for (int j = 0; j < n; j++) {
int a0 = 0, a1 = 0, b0 = 0, b1 = 0;
// 读入数据
a0 = sc.nextInt();
a1 = sc.nextInt();
b0 = sc.nextInt();
b1 = sc.nextInt();
int cnt = 0; // 满足条件的x的数目
int sqrt = (int) Math.sqrt(b1);
// 暴力枚举
for (int i = 1; i <= sqrt; i++) {
// i必为b1的因子
if (b1 % i == 0) {
if (i % a1 == 0 && gcd(i / a1, a0 / a1) == 1 && gcd(b1 / b0, b1 / i) == 1)
cnt++;
int y = b1 / i; // 得到另一个因子
if (i == y)
continue;
if (y % a1 == 0 && gcd(y / a1, a0 / a1) == 1 && gcd(b1 / b0, b1 / y) == 1)
cnt++;
}
}
System.out.println(cnt);
}
sc.close();
}
// 最大公约数
public static int gcd(int a, int b) {
int max, min;
max = (a > b) ? a : b;
min = (a < b) ? a : b;
if (max % min != 0) {
return gcd(min, max % min);
} else
return min;
}
// 最小公倍数
public static int lcm(int a, int b) {
return a * b / gcd(a, b);
}
}