题目大意
本题为填空题,只需要算出结果后,在代码中使用输出语句将所填结果输出即可。
⭐⭐在平面直角坐标系中,两点可以确定一条直线。
⭐⭐给定平面上 20 × 21 个整点 {(x, y)|0 ≤ x < 20, 0 ≤ y < 21, x ∈ Z, y ∈ Z},即横坐标是 0到 19 (包含 0 和 19) 之间的整数、纵坐标是 0 到 20 (包含 0 和 20) 之 间的整数的点。
请问这些点一共确定了多少条不同的直线?
答案:40257
解题思路
⭐⭐初高中学过直线的多种表达形式,这里选用点斜式,通过比较k和b区分直线。用java的话,我们可以把k和b存入HashSet中,使得重复的直线只储存一个数据。
⭐⭐关键点及注意事项:
🌙 斜率不存在的直线要另外考虑
🌙k和b可能是小数,而小数在计算机中是有精度的,直接存小数,哪怕用double型也可能不准确。所以我们用分数存储k和b,即k=kup/kdown;b=bup/bdown;且要用最简分数表示,否则不好比较是否相同。其中b可以通过一个点和k计算而得。
🌙以kup+" “+Kdown+” “+bup+” "+bdown的字符串形式将直线存入HashSet。没有斜率的直线用x=n的形式存储
🌙求最简分数可以先通过求分子分母的最大公约数,再同时除以最大公约数。我的代码中最大公约数用辗转相除法求得。
🌙把坐标存入容器ArrayList中,形式是x*100+y(一个int型的数据)这个数对除以100就是横坐标,对100取余就是纵坐标。然后再一个双重循环,就可以计算直线了。
代码解析
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
public class Main {
public static void main(String[] args) {
HashSet<String> kb=new HashSet<String>();//存k和b(kup kdown bup bdown)
int x=0;//横坐标
int y=0;//纵坐标
/*把坐标以x*100+y的形式存入ArrayList*/
HashSet<Integer> al=new HashSet<>();
for(x=0;x<20;x++){
for(y=0;y<21;y++){
al.add(x*100+y);
}
}
List<Integer> arr = new ArrayList<>(al);
//用点斜式标识一条直线
for(int i=0;i<x*y;i++){
int x1=arr.get(i)/100;//点一横坐标
int y1=arr.get(i)%100;//点一纵坐标
for(int j=i+1;j<x*y;j++){
int x2=arr.get(j)/100;//点二横坐标
int y2=arr.get(j)%100;//点二纵坐标
/*计算斜率(用分数(kup/kdown)表示)*/
int kup=y1-y2;
int kdown=x1-x2;
if(kdown==0){
//kdown=0说明斜率不存在
String s="y="+x1;
kb.add(s);
}else{
int kgcd=gcd(kup,kdown);//分子分母最大公约数
kup=kup/kgcd;
kdown=kdown/kgcd;//得到最简分数
//计算截距(用分数(bup/bdown)表示)
int bup=y1*kdown-kup*x1;
int bdown=kdown;
int bgcd=gcd(bup,bdown);//分子分母最大公约数
bup=bup/bgcd;
bdown=bdown/bgcd;//得到最简分数
//以kup+" "+Kdown+" "+bup+" "+bdown的形式存储一条直线
String s=kup+" "+kdown+" "+bup+" "+bdown;
kb.add(s);
}
}
}
//kb中的元素是不重复的,有重复也只会存储其中一个,因此kb的大小就是直线的个数
System.out.println(kb.size());
}
//辗转相除求最大公约数
static int gcd(int a, int b) {
if(b==0){
return a;
}
return gcd(b,a%b);
}
}