nchu-software-oop-2022-3
前言
一些话
- 这次的题目对浮点型精度的要求较高,需要考虑如何才能让精度不丢失;
- 且需要大量用到点、线(线段),三角形的一些操作,所以建议先封装好这三大类;
- 对于非法串的过滤需要用到正则表达式(我是嫖的);
- 浮点数一直挺坑人的,判断两个浮点数是否相等最好是判断他们的差值的绝对值是否小于一个近似无穷小的值,比如(0.000001); 其实第一次练习题就有过了(古巴比伦人求近似值那题)
Point 类
属性:
double类型的(x,y);
方法:
Point();Point(double a,double c); //析构函数
void print(); //打印函数
double distance(Point another); //求两点距离
boolean isSameTo(Point a); //判断两点是否重合
double Slope(Point another); //求两点斜率
double dis(Line l); //求点到直线的距离
求点到直线的距离最好是用距离公式,若用海伦公式求三角形面积再求高(距离)误差过大;
boolean inLine(Point a1,Point a2); //求三点是否在同一直线上
代码如下
package Points;
public class Point{
public double x, y;
/*两个析构函数可以使得在构造时有两个选择
例如 Point p = new Point();
Point p = new Point(1.14,5.14);
都是合法的,这样使得构造时更加灵活。*/
public Point(){
this.x = 0;
this.y = 0;
}
public Point(double a,double b){
this.x = a;
this.y = b;
}
public void print(){ //打印
System.out.printf("(%f,%f)\n",this.x,this.y);
}
//判断两点是否相同
public boolean isSameTo(Point a){
return (this.x == a.x)&&(this.y == a.y);
}
//两点距离
public double distance(Point another){
return Math.sqrt(Math.pow(this.x-another.x,2) + Math.pow(this.y-another.y,2));
}
//两点斜率(斜率无穷大则返回2^48)
public double Slope(Point another){
if(this.x == another.x)
return 2^48;
return ((this.y - another.y)) / ((this.x - another.x));
}
//点到直线的垂直距离(距离公式)
public double dis(Line l){
return Math.abs(l.a*this.x+l.b*this.y+l.c) / Math.sqrt(Math.pow(l.a,2)+Math.pow(l.b,2));
}
//三点在同一条直线上(Line)详见下一个类;
public boolean inLine(Point a1,Point a2){
Line l = new Line(a1,a2);
return this.dis(l)==0;
}
}
Line 类(线段)
继承于
Point
属性
public Point sta, ed; //线段的其中两点,用于求出一般式
public double a,b,c; //aX + bY + c = 0;
方法
Line(Point a,Point b);//析构函数
void print();//打印函数
double length();//求两端点距离
boolean isCrawlTo(Line another);//判断两线段是否平行
boolean isSameTo(Line another);//判断两线段是否重合
Point getIntersection(Line another);//求两线段的交点
boolean inLineSegment(Point p);//判断某点是否在线段两个交点之间(不包括端点)
boolean inLineSegment_close(Point p);//同上,但是包括了端点
boolean inLine(Point p);//判断某点是否在直线上
代码如下
package Points;
public class Line extends Point {
public Point sta, ed;
public double a,b,c;
public Line(Point a,Point b){
this.sta = a;
this.ed = b;
this.a = (-(a.y-b.y));
this.b = (a.x-b.x);
this.c = (-this.a*this.sta.x-this.b*this.sta.y);
}
public void print(){
System.out.printf("%fX + %fY + %f = 0\n",this.a,this.b,this.c);
}
public double length(){
return this.sta.distance(this.ed);
}
/*判断是否平行
注意要特判b为0的情况,因为b在之后作为了除数*/
public boolean isCrawlTo(Line another){
if(this.b==0 || another.b==0){
return (this.b == 0 && another.b == 0);
}
return ((this.a / this.b) == (another.a / another.b));
}
//判断是否重合(平行+c值相等)
public boolean isSameTo(Line another){
return this.isCrawlTo(another) && (this.c==another.c);
}
//求两条直线交点(求交点的一个公式,无非就是联立两方程之后求解)
public Point getIntersection(Line another){
//特判平行的情况,返回null值
if(this.isCrawlTo(another)){
return null;
}
Point res = new Point();
res.y = (another.a*this.c-this.a*another.c) / (this.a*another.b-another.a*this.b);
res.x = (this.b*another.c-another.b*this.c) / (this.a*another.b-another.a*this.b);
return res;
}
//判断某点是否在线段之内(不包括端点)
public boolean inLineSegment(Point p){
//其实还需要p.inline(this)判断一下点是否在直线之上,偷个懒,但是也能过题
if(!this.inLine(p)) return false;
if(p.x == this.sta.x && p.y == this.sta.y) return false;
if(p.x == this.ed.x && p.y == this.ed.y) return false;
double res = p.distance(this.sta) + p.distance(this.ed) - this.length();
//
return (Math.abs(res) < 0.000001);
}
//判断点是否在线段之内(包括端点)
public boolean inLineSegment_close(Point p){
if(!this.inLine(p)) return false;
double res = p.distance(this.sta) + p.distance(this.ed) - this.length();
return (Math.abs(res) < 0.000001);
}
//某点在直线之上
public boolean inLine(Point p){
return Math.abs(this.a*p.x + this.b*p.y + this.c) <0.000001;
}
}
Triangle 类
继承于
Point
属性
public Point a,b,c; //三个顶点
public Line ab,ac,bc; //三条边
方法
Triangle(Point a, Point b, Point c);//析构函数
void print();//打印函数
boolean isIsoscelesTriangle();//判断是否为等腰三角形
boolean isEquilateralTriangle();//判断是否为等边三角形
double sideLength();//求三角形边长
double area();//求三角形面积
Point focusPoint();//求三角形重心坐标
int type();//求三角形类型(锐角0,直角1,钝角2)
int isContainPoint(Point p);//判断某点相对三角形的位置(内0,边1,外2)
代码如下
package Points;
import java.util.Arrays;
public class Triangle extends Point {
public Point a,b,c;
public Line ab,ac,bc;
//析构函数
public Triangle(Point a, Point b, Point c){
this.a = a;
this.b = b;
this.c = c;
this.ab = new Line(a,b);
this.ac = new Line(a,c);
this.bc = new Line(b,c);
}
public void print(){
this.a.print();
this.b.print();
this.c.print();
}
//判断是否为等腰三角形(任意两边相等,浮点数最好慎用 == )
public boolean isIsoscelesTriangle(){
return this.ab.length() == this.ac.length() ||
this.ab.length() == this.bc.length() ||
this.ac.length() == this.bc.length();
}
//判断是否为等边三角形(三边相等)
public boolean isEquilateralTriangle(){
return this.ab.length() == this.ac.length() &&
this.ac.length() == this.bc.length();
}
//求三角形的边长(三边长度求和)
public double sideLength(){
return this.ab.length() + this.ac.length() + this.bc.length();
}
//求三角形面积(海伦公式)
public double area(){
double p = this.sideLength() / 2;
return Math.sqrt(p*(p-this.ab.length())*(p-this.ac.length())*(p-this.bc.length()));
}
//求三角形的重心(重心公式)
public Point focusPoint(){
Point res = new Point();
res.x = (this.a.x + this.b.x + this.c.x) / 3;
res.y = (this.a.y + this.b.y + this.c.y) / 3;
return res;
}
//求三角形类型,(锐角0,直角1,钝角2)
// A2 + B2 (<=>) C2
public int type(){
double[] num = new double[3];
num[0] = this.ab.length();num[1] = this.ac.length();num[2] = this.bc.length();
Arrays.sort(num);
double tmp = Math.pow(num[0],2) + Math.pow(num[1],2) - Math.pow(num[2],2);
if(Math.abs(tmp) < 0.0000001) return 1;
if(tmp < 0) return 2;
return 0;
}
// 判断某点是否在三角形之内,(0内,1边,2外);
//面积公式判断即可,不用啥啥射线法
public int isContainPoint(Point p){
Triangle t1 = new Triangle(this.a,this.b,p);
Triangle t2 = new Triangle(this.b,this.c,p);
Triangle t3 = new Triangle(this.c,this.a,p);
double s = this.area() - t1.area() - t2.area() - t3.area();
if(Math.abs(t1.area()) < 0.000001 || Math.abs(t2.area()) < 0.000001 || Math.abs(t3.area()) < 0.000001)
return 1;
if(Math.abs(s) < 0.000001) return 0;
return 2;
}
}
7-1 点线形系列1-计算两点之间的距离
输入连个点的坐标,计算两点之间的距离
输入格式:
4个double类型的实数,两个点的x,y坐标,依次是x1、y1、x2、y2,两个点的坐标之间以空格分隔,每个点的x,y坐标以英文“,”分隔。例如:0,0 1,1或0.1,-0.3 +3.5,15.6。
若输入格式非法,输出"Wrong Format"。
若输入格式合法但坐标点的数量超过两个,输出“wrong number of points”。
输出格式:
计算所得的两点之间的距离。例如:1.4142135623730951
输入输出样例
太多了555555
思路:
- 采用String.split函数将输入串分割;(将会生成一个tmpstr数组),其中数组长度代表值的个数(不合法输出“wrong number of points”)
- 采用正则表达式对数组中的每一个进行判断,有不满足的就输出“Wrong format”
- 采用Double.parsedouble函数将数组中的每一个转换为double型的num数组
- 将num数组中的值赋值给Point型的p数组
- 调用Point.distance即可
实现:
package nchu_software_oop_2022_3;
import java.util.*;
import Points.*;
public class p_1 {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
String str = sc.nextLine();
//将串转化为浮点型
String[] tmpstr = str.split(" |,");
double[] num = new double[30];
int cnt = 0;
for (String a : tmpstr) {
if (!check(a)) {
System.out.println("Wrong Format");
return;
}
num[cnt++] = Double.parseDouble(a);
}
//将浮点型转化为坐标点型
Point[] p = new Point[10];
for (int i = 0; i < cnt; i += 2) {
p[i / 2] = new Point(num[i], num[i + 1]);
}
//特判
if(cnt != 4){
System.out.println("wrong number of points");
return;
}
//ans
System.out.println(p[0].distance(p[1]));
}
public static boolean check(String str){
return str.matches("^[+-]?(0|(0\\.\\d+)?|[1-9][0-9]*(\\.\\d+)?)$");
}
public static String cnm(double a){
String res = String.format("%.6f",a);
res = res.replaceAll("0+?$", "");
if(res.charAt(res.length()-1) == '.') res+='0';
return res;
}
}
7-2 点线形系列2-线的计算
用户输入一组选项和数据,进行与直线有关的计算。选项包括:
1:输入两点坐标,计算斜率,若线条垂直于X轴,输出"Slope does not exist"。
2:输入三个点坐标,输出第一个点与另外两点连线的垂直距离。
3:输入三个点坐标,判断三个点是否在一条线上,输出true或者false。
4:输入四个点坐标,判断前两个点所构成的直线与后两点构成的直线是否平行,输出true或者false.
5:输入四个点坐标,计算输出前两个点所构成的直线与后两点构成的直线的交点坐标,x、y坐标之间以英文分隔",“,并输出交叉点是否在两条线段之内(不含四个端点)的判断结果(true/false),判断结果与坐标之间以一个英文空格分隔。若两条线平行,没有交叉点,则输出"is parallel lines,have no intersection point”。
输入格式
基本格式:选项+“:”+坐标x+“,”+坐标y+" “+坐标x+”,“+坐标y。
例如:1:0,0 1,1
如果不符合基本格式,输出"Wrong Format”。
如果符合基本格式,但输入点的数量不符合要求,输出"wrong number of points"。
不论哪个选项,如果格式、点数量都符合要求,但构成任一条线的两个点坐标重合,输出"points coincide",
输出格式
见题目描述。 什么鬼输出格式:(
输入输出样例
太多了55555555555
思路:
- 前4点和第一题一致,只不过加了一个正则表达式;
(以下不在赘述非法情况) - cmd == 1: 调用Point.Slope函数,特判返回值为2^48(斜率无穷大)的情况
- cmd == 2:调用Point.dis函数
- cmd == 3:调用Point.inLine函数
- cmd == 4:调用Line.isCrawlTo函数
- cmd == 5:先调用Line.isCrawlTo函数特判平行,在调用Line.getIntersection函数获取两线段交点,再调用Line.inLineSegment函数判断交点是否在两条线段的任一条内
实现
package nchu_software_oop_2022_3;
import java.util.*;
import Points.*;
public class p_2 {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
String str = sc.nextLine();
//判断输入格式是否正确
if(!str.matches("^[1-5][:](([+-]?(0|(0\\.\\d+)|[1-9][0-9]*(\\.\\d+)?))[,]([+-]?(0|(0\\.\\d+)|[1-9][0-9]*(\\.\\d+)?))\\s?)+$")){
System.out.println("Wrong Format");
return;
}
//取出cmd,将串转化为浮点型
int cmd = str.charAt(0)-'0';
str = str.substring(2).trim();
String[] tmpstr = str.split(" |,");
double[] num = new double[30];
int cnt = 0;
for(String aa:tmpstr){
if(!check(aa)){
System.out.println("Wrong Format");
return;
}
num[cnt++] = Double.parseDouble(aa);
}
//将浮点型转化为坐标点型
Point[] p = new Point[10];
for(int i=0;i<cnt;i+=2){
p[i/2] = new Point(num[i],num[i+1]);
}
//特判(判断掉那些点数非法的输入)
if(cmd==1){
if(cnt != 4){
System.out.println("wrong number of points");
return ;
}
}
if(cmd==2 || cmd==3){
if(cnt!=6){
System.out.println("wrong number of points");
return ;
}
}
if(cmd==4 || cmd==5){
if(cnt!=8){
System.out.println("wrong number of points");
return ;
}
}
//题目要求(无脑if)
if(cmd==1){
//坐标点重合
if(p[0].isSameTo(p[1])){
System.out.println("points coincide");
return ;
}
Line l = new Line(p[0],p[1]);
//l.b为0说明直线没有斜率
if(Math.abs(l.b) <=0.0001){
System.out.println("Slope does not exist");
return;
}
System.out.println(p[0].Slope(p[1]));
}
if(cmd==2){
if(p[1].isSameTo(p[2])){
System.out.println("points coincide");
return ;
}
Line l = new Line(p[1],p[2]);
System.out.println(p[0].dis(l));
}
if(cmd==3){
if(p[1].isSameTo(p[2])){
System.out.println("points coincide");
return ;
}
System.out.println(p[0].inLine(p[1],p[2]));
}
if(cmd==4){
if(p[0].isSameTo(p[1]) || p[2].isSameTo(p[3])){
System.out.println("points coincide");
return ;
}
Line l1 = new Line(p[0],p[1]);
Line l2 = new Line(p[2],p[3]);
System.out.println(l1.isCrawlTo(l2));
}
if(cmd==5){
if(p[0].isSameTo(p[1]) || p[2].isSameTo(p[3])){
System.out.println("points coincide");
return ;
}
Line l1 = new Line(p[0],p[1]);
Line l2 = new Line(p[2],p[3]);
//特判平行
if(l1.isCrawlTo(l2)){
System.out.println("is parallel lines,have no intersection point");
return ;
}
Point tmp = l1.getIntersection(l2); //交点坐标
//注意是 或
boolean t = l2.inLineSegment(tmp)||l1.inLineSegment(tmp); //交点在任一线段之内
System.out.println(cnm(tmp.x)+","+cnm(tmp.y)+" "+t);
}
}
//正则表达式判断输入合法性
public static boolean check(String str){
// System.out.println("debug"+str);
return str.matches("^[+-]?(0|(0\\.\\d+)?|[1-9][0-9]*(\\.\\d+)?)$");
}
//保留六位小数(其实是第三题的要求)
public static String cnm(double a){
String res = String.format("%.6f",a);
res = res.replaceAll("0+?$", "");
if(res.charAt(res.length()-1) == '.') res+='0';
return res;
}
}
7-3 点线形系列3-三角形的计算
用户输入一组选项和数据,进行与三角形有关的计算。选项包括:
1:输入三个点坐标,判断是否是等腰三角形、等边三角形,判断结果输出true/false,两个结果之间以一个英文空格符分隔。
2:输入三个点坐标,输出周长、面积、重心坐标,三个参数之间以一个英文空格分隔,坐标之间以英文",“分隔。
3:输入三个点坐标,输出是钝角、直角还是锐角三角形,依次输出三个判断结果(true/false),以一个英文空格分隔,
4:输入五个点坐标,输出前两个点所在的直线与三个点所构成的三角形相交的交点数量,如果交点有两个,则按面积大小依次输出三角形被直线分割成两部分的面积。若直线与三角形一条线重合,输出"The point is on the edge of the triangle”
5:输入四个点坐标,输出第一个是否在后三个点所构成的三角形的内部(输出in the triangle/outof triangle)。
必须使用射线法,原理:由第一个点往任一方向做一射线,射线与三角形的边的交点(不含点本身)数量如果为1,则在三角形内部。如果交点有两个或0个,则在三角形之外。若点在三角形的某条边上,输出"on the triangle"
输入格式
基本格式:选项+“:”+坐标x+“,”+坐标y+" “+坐标x+”,“+坐标y。点的x、y坐标之间以英文”,“分隔,点与点之间以一个英文空格分隔。
输出格式
基本输出格式见每种选项的描述。
异常情况输出:
如果不符合基本格式,输出"Wrong Format”。
如果符合基本格式,但输入点的数量不符合要求,输出"wrong number of points"。
如果输入的三个点无法构成三角形,输出"data error"。
注意:输出的数据若小数点后超过6位,只保留小数点后6位,多余部分采用四舍五入规则进到最低位。小数点后若不足6位,按原始位数显示,不必补齐。例如:1/3的结果按格式输出为 0.333333,1.0按格式输出为1.0
选项4中所输入线的两个点坐标重合,输出"points coincide",
输入输出样例
太多了!!!!!55555555555555555555555
思路
- 前几点和前两题差不多,相当于初始化操作
- cmd == 1:调用Triangle.isIsoscelesTriangle函数 和 Triangle.isEquilateralTriangle函数即可
- cmd == 2:调用三角形类里面求周长,面积,周长的函数即可
- cmd == 3:调用Triangle.type函数即可
- cmd == 4:
5.1. 先调用Line.isSameTo函数特判直线与三角形任一边重合的情况
5.2. 求出直线与三条边各自的交点,并求出交点是否各自位于对应的边内(Boolean)(不包括端点);
5.3. 特判直线交于三角形任一角的情况(like this):
5.4. 以上完毕之后,只剩下交点为2个和0个的情况(like this):
6. cmd == 5:调用Triangle.isContainPoint函数即可
实现
package nchu_software_oop_2022_3;
import java.util.*;
import Points.*;
public class p_3 {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
String str = sc.nextLine();
//判断输入格式是否正确
if(!str.matches("^[1-5][:](([+-]?(0|(0\\.\\d+)|[1-9][0-9]*(\\.\\d+)?))[,]([+-]?(0|(0\\.\\d+)|[1-9][0-9]*(\\.\\d+)?))\\s?)+$")){
System.out.println("Wrong Format");
return;
}
//取出cmd,将串转化为浮点型
int cmd = str.charAt(0)-'0';
str = str.substring(2).trim();
String[] tmpstr = str.split(" |,");
double[] num = new double[30];
int cnt = 0;
for(String a:tmpstr){
if(!check(a)){
System.out.println("Wrong Format");
return;
}
num[cnt++] = Double.parseDouble(a);
}
//将浮点型转化为坐标点型
Point[] p = new Point[10];
for(int i=0;i<cnt;i+=2){
p[i/2] = new Point(num[i],num[i+1]);
}
//特判(坐标点数量非法)
if(cmd==1|| cmd==2 || cmd==3){
if(cnt != 6){
System.out.println("wrong number of points");
return;
}
Triangle t = new Triangle(p[0],p[1],p[2]);
if(t.area() == 0){
System.out.println("data error");
return;
}
}
if(cmd==4){
if(cnt != 10){
System.out.println("wrong number of points");
return;
}
Triangle t = new Triangle(p[2],p[3],p[4]);
if(t.area() == 0){
System.out.println("data error");
return;
}
if(p[0].isSameTo(p[1])){
System.out.println("points coincide");
return;
}
}
if(cmd==5){
if(cnt != 8){
System.out.println("wrong number of points");
return;
}
Triangle t = new Triangle(p[1],p[2],p[3]);
if(t.area() <= 0.01){
System.out.println("data error");
return;
}
}
//题目要求
if(cmd == 1){
Triangle t = new Triangle(p[0],p[1],p[2]);
System.out.printf("%s %s\n",t.isIsoscelesTriangle(),t.isEquilateralTriangle());
}
if (cmd == 2){
Triangle t = new Triangle(p[0],p[1],p[2]);
System.out.printf("%s %s %s,%s\n",cnm(t.sideLength()),cnm(t.area()),cnm(t.focusPoint().x),cnm(t.focusPoint().y));
}
if (cmd == 3){
Triangle t = new Triangle(p[0],p[1],p[2]);
System.out.printf("%s %s %s",t.type()==2,t.type()==1,t.type()==0);
}
if (cmd == 4){
Line l = new Line(p[0],p[1]);
Triangle t = new Triangle(p[2],p[3],p[4]);
//与任意一条边重合
if(l.isSameTo(t.ab) || l.isSameTo(t.ac) || l.isSameTo(t.bc)){
System.out.println("The point is on the edge of the triangle");
return;
}
//与三条边的交点(值可能为null,即平行)
Point p_ab = l.getIntersection(t.ab);
Point p_ac = l.getIntersection(t.ac);
Point p_bc = l.getIntersection(t.bc);
//三交点是否位于边之内
boolean p_ab_in=false, p_ac_in =false, p_bc_in=false;
if(p_ab != null) p_ab_in = t.ab.inLineSegment(p_ab);
if(p_ac != null) p_ac_in = t.ac.inLineSegment(p_ac);
if(p_bc != null) p_bc_in = t.bc.inLineSegment(p_bc);
//任一角在直线之上(特判三角形的角)
if(l.inLine(t.a)){
//与另一条边无交点或者交点在边之外
if(p_bc == null || !t.bc.inLineSegment_close(p_bc)){
System.out.println("1");
}
else pr_ans(t,t.a,t.b,p_bc);
return;
}
if(l.inLine(t.b)){
if(p_ac == null || !t.ac.inLineSegment_close(p_ac)){
System.out.println("1");
}
else pr_ans(t,t.a,t.b,p_ac);
return;
}
if(l.inLine(t.c)){
if(p_ab == null || !t.ab.inLineSegment_close(p_ab)){
System.out.println("1");
}
else pr_ans(t,t.a,t.c,p_ab);
return;
}
//两个交点
if(p_ab_in && p_bc_in){ pr_ans(t,t.b,p_ab,p_bc);return;}
if(p_ab_in && p_ac_in){ pr_ans(t,t.a,p_ab,p_ac);return;}
if(p_bc_in && p_ac_in){ pr_ans(t,t.c,p_bc,p_ac);return;}
//无交点
System.out.println("0");
}
if (cmd == 5){
Triangle t = new Triangle(p[1],p[2],p[3]);
int v = t.isContainPoint(p[0]);
if(v == 0) System.out.println("in the triangle");
if(v == 1) System.out.println("on the triangle");
if(v == 2) System.out.println("outof the triangle");
}
}
public static boolean check(String str){
// System.out.println("debug"+str);
return str.matches("^[+-]?(0|(0\\.\\d+)?|[1-9][0-9]*(\\.\\d+)?)$");
}
public static void pr_ans(Triangle t, Point a,Point b,Point c){
Triangle tmp_t =new Triangle(a,b,c);
double[] ans = new double[2];
ans[0] = tmp_t.area();
ans[1] = t.area() - tmp_t.area();
Arrays.sort(ans);
System.out.printf("2 %s %s\n",cnm(ans[0]),cnm(ans[1]));
}
public static String cnm(double a){
String res = String.format("%.6f",a);
res = res.replaceAll("0+?$", "");
if(res.charAt(res.length()-1) == '.') res+='0';
return res;
}
}
一些废话
上面是结构,那些代码若是直接提交过不了 😃