期中考试与第二次大作业分析
前言
本次实验是对第4与第5次作业的的题目总结,相比上一次的课程作业,这次更加聚焦与对与面向对象的梳理与使用,深入理解继承与多态。相比上一次的作业整体题目难度显著提高,所以编写代码的时间也较多。题目主要聚焦与对于四边形与五边形类的设计与使用,在第一次完成代码后,我对于代码整体构造并不是很满意,所以也做了几次版本的修改,最终达到了一个自己相较满意的样子。在几次作业之后,也进行了期中考试测验,测验难度比较基础,考察对于基础、多态和容器类的基本构建与认知,在本次博客中会一同进行分析。
设计与分析
期中考试
7-1 点与线(类设计)
首先对于期中考试进行分析,第一道是关于点与线的基本设计,题目要求如下:
-
设计一个类表示平面直角坐标系上的点Point,私有属性分别为横坐标x与纵坐标y,数据类型均为实型数,除构造方法以及属性的getter与setter方法外,定义一个用于显示信息的方法display(),用来输出该坐标点的坐标信息,格式如下:
(x,y),数值保留两位小数。为简化题目,其中,坐标点的取值范围设定为(0,200]。若输入有误,系统则直接输出Wrong Format - 设计一个类表示平面直角坐标系上的线Line,私有属性除了标识线段两端的点point1、point2外,还有一个字符串类型的color,用于表示该线段的颜色,同样,除构造方法以及属性的getter与setter方法外,定义一个用于计算该线段长度的方法getDistance(),还有一个用于显示信息的方法display(),用来输出线段的相关信息
首先设计一个Point类
public class Point {
private double x;
private double y;
Point(double x, double y) {
if (x > 200 | y > 200 | x <= 0 | y <= 0) {
System.out.println("Wrong Format");
System.exit(0);
}
this.x = x;
this.y = y;
}
public double getX() {
return x;
}
public void setX(double x) {
this.x = x;
}
public double getY() {
return y;
}
public void setY(double y) {
this.y = y;
}
public void display() {
System.out.printf("(%.2f,%.2f)\n", x, y);
}
}
对于Point类设计,满足题目要求,将相关属性进行私密化,并提高相应的getter以及setter,对与整体类应该构造一个Point构造器,需要传入两个double类型参数,给与点分别赋予为横坐标X与纵坐标Y,唯一需要注意的是,构造器中要对传入的数据进行范围判定,若是超出范围,便需要设计产生错误提示与调用System的exit方法对与程序终止。设计完成Point类后,就可以进行对于Line类的设计
public class Line {
private Point point1;
private Point point2;
private String color;
public Line(Point point1, Point point2) {
this.point1 = point1;
this.point2 = point2;
}
public Point getPoint1() {
return point1;
}
public Point getPoint2() {
return point2;
}
public String getColor() {
return color;
}
public void setColor(String color) {
this.color = color;
}
public void setPoint1(Point point1) {
this.point1 = point1;
}
public void setPoint2(Point point2) {
this.point2 = point2;
}
public double getDistance() {
return Math.sqrt(Math.pow(point1.getX() - point2.getX(), 2) + Math.pow(point1.getY() - point2.getY(), 2));
}
public void display() {
System.out.println("The line's color is:" + color);
System.out.println("The line's begin point's Coordinate is:");
point1.display();
System.out.println("The line's end point's Coordinate is:");
point2.display();
System.out.printf("The line's length is:%.2f", getDistance());
}
}
Line类设计大致与Point无二,注意相关属性私有化即可,对于Line而言只要向构造器传入两个Point类的点即可构成一根线,需要注意的是display方法,getDistance中调用Point类属性需要使用getter方法以及对于调用getDistance返回的结果需要格式化输出,满足相关输出规定,最后设计相关Main类实例化Point与Line即可
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
Point point1 = new Point(scanner.nextDouble(), scanner.nextDouble());
Point point2 = new Point(scanner.nextDouble(), scanner.nextDouble());
Line line = new Line(point1, point2);
line.setColor(scanner.next());
line.display();
}
}
输出样例结果如下

本道题目设计比较基础,主要是考察对于类的基本实例化与使用,使用属性加密是为了让类内属性更具有安全性,在工业设计上是一直常见的要求,本题的SourceMonitor的生成报表如下,因为题目简单,所以代码复杂度和深度都较低。

7-2 点线面问题重构(继承与多态)
本道题目在“点与线(类设计)”题目基础上,对题目的类设计进行重构,以实现继承与多态的技术性需求,具体题目如下
- 对题目中的点Point类和线Line类进行进一步抽象,定义一个两个类的共同父类Element(抽象类),将display()方法在该方法中进行声明(抽象方法),将Point类和Line类作为该类的子类。
- 再定义一个Element类的子类面Plane,该类只有一个私有属性颜色color,除了构造方法和属性的getter、setter方法外,display()方法用于输出面的颜色,输出格式如下:
The Plane's color is:颜色 - 在主方法内,定义两个Point(线段的起点和终点)对象、一个Line对象和一个Plane对象,依次从键盘输入两个Point对象的起点、终点坐标和颜色值(Line对象和Plane对象颜色相同),然后定义一个Element类的引用,分别使用该引用调用以上四个对象的display()方法,从而实现多态特性。
对于题目要求,应该先设计一个Element的抽象父类统一考察
public abstract class Element {
public abstract void display();
}
因为子类的Point、Line以及Plane都拥有共同的方法display,所以可以在抽象父类Element中集中定义,Pont类、和Line类和一道题目相似,唯一注意是向共同方法display前添加一行
@Override
@Override 的作用是:如果想重写父类的方法,比如toString()方法的话,在方法前面加上@Override 系统可以帮你检查方法的正确性。根据题目要求还要构建Plane类
public class Plane extends Element {
private String color;
public Plane(String color){
this.color=color;
}
public String getColor() {
return color;
}
public void setColor(String color) {
this.color = color;
}
@Override
public void display() {
System.out.println("The Plane's color is:" + color);
}
}
以下是Main方法和输出结果
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
Point point1 = new Point(scanner.nextDouble(), scanner.nextDouble());
Point point2 = new Point(scanner.nextDouble(), scanner.nextDouble());
String color = scanner.next();
Line line = new Line(point1, point2, color);
Plane plane = new Plane(color);
Element element;
element = point1;//起点Point
element.display();
element = point2;//终点Point
element.display();
element = line;//线段
element.display();
element = plane;//面
element.display();
}
}

本道题目相较于上一道题目更多的就是考虑了继承和多态的使用,但也比较简单,SourceMonitor的生成报表如下,使用继承的意义是(1)提高代码的重用性。(2)优化代码,方便修改。(3)类与类之间产生了关系,是多态的前提。而多态可以(1)提高了代码的维护性 (通过继承和实现来保证) (2)提高了代码的扩展性 (通过多态来保证) (3)降低代码耦合度

7-3 点线面问题再重构(容器类)
在“点与线(继承与多态)”题目基础上,对题目的类设计进行重构,增加容器类保存点、线、面对象,并对该容器进行相应增、删、遍历操作,题目内容如下
- 在原有类设计的基础上,增加一个GeometryObject容器类,其属性为
ArrayList<Element>类型的对象(若不了解泛型,可以不使用<Element>) - 增加该类的
add()方法及remove(int index)方法,其功能分别为向容器中增加对象及删除第index - 1(ArrayList中index>=0)个对象 - 在主方法中,用户循环输入要进行的操作(choice∈[0,4]),其含义如下:
- 1:向容器中增加Point对象
- 2:向容器中增加Line对象
- 3:向容器中增加Plane对象
- 4:删除容器中第index - 1个数据,若index数据非法,则无视此操作
- 0:输入结束
关于父类Element和子类Piont、Line、Plane的类关系基本不需要去改变,下面先给出Main方法与结果后再做分析
import java.util.ArrayList;
import java.util.Scanner;
public class Main2 {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
ArrayList<Element> elements = new ArrayList<>();
int choice = scanner.nextInt();
while (choice != 0) {
switch (choice) {
case 1:
elements.add(new Point(scanner.nextDouble(), scanner.nextDouble()));
break;
case 2:
Point point1 = new Point(scanner.nextDouble(), scanner.nextDouble());
Point point2 = new Point(scanner.nextDouble(), scanner.nextDouble());
elements.add(new Line(point1, point2, scanner.next()));
break;
case 3:
elements.add(new Plane(scanner.next()));
break;
case 4:
int index = scanner.nextInt() - 1;
if (index >= 0 && index < elements.size()) elements.remove(index);
break;
}
choice = scanner.nextInt();
}
elements.forEach(Element::display);
}
}

elements.forEach(Element::display)forEach() 方法用于遍历动态数组中每一个元素并执行特定操作。后面接着一个lamdba表达式的简写,elements.forEach(element->element.display())是它的原型。SourceMonitor的生成报表如下

PTA作业
7-2 点线形系列4-凸四边形的计算
本道题目上一次blog作业之后的继续难度加强,题目内容如下
用户输入一组选项和数据,进行与四边形有关的计算。
以下四边形顶点的坐标要求按顺序依次输入,连续输入的两个顶点是相邻顶点,第一个和最后一个输入的顶点相邻。
选项包括:
1:输入四个点坐标,判断是否是四边形、平行四边形,判断结果输出true/false,结果之间以一个英文空格符分隔。
2:输入四个点坐标,判断是否是菱形、矩形、正方形,判断结果输出true/false,结果之间以一个英文空格符分隔。 若四个点坐标无法构成四边形,输出"not a quadrilateral"
3:输入四个点坐标,判断是凹四边形(false)还是凸四边形(true),输出四边形周长、面积,结果之间以一个英文空格符分隔。 若四个点坐标无法构成四边形,输出"not a quadrilateral"
4:输入六个点坐标,前两个点构成一条直线,后四个点构成一个四边形或三角形,输出直线与四边形(也可能是三角形)相交的交点数量。如果交点有两个,再按面积从小到大输出四边形(或三角形)被直线分割成两部分的面积(不换行)。若直线与四边形或三角形的一条边线重合,输出"The line is coincide with one of the lines"。若后四个点不符合四边形或三角形的输入,输出"not a quadrilateral or triangle"。
后四个点构成三角形的情况:假设三角形一条边上两个端点分别是x、y,边线中间有一点z,另一顶点s:
1)符合要求的输入:顶点重复或者z与xy都相邻,如x x y s、x z y s、x y x s、s x y y。此时去除冗余点,保留一个x、一个y。
2) 不符合要求的输入:z 不与xy都相邻,如z x y s、x z s y、x s z y
5:输入五个点坐标,输出第一个是否在后四个点所构成的四边形(限定为凸四边形,不考虑凹四边形)或三角形(判定方法见选项4)的内部(若是四边形输出in the quadrilateral/outof the quadrilateral,若是三角形输出in the triangle/outof the triangle)。如果点在多边形的某条边上,输出"on the triangle或者on the quadrilateral"。若后四个点不符合四边形或三角形,输出"not a quadrilateral or triangle"。
本道题目是我第一次去思考类与类的协同关系,所以在设计方面有很多令我感到不满意的地方,此处我先还原第一次思路,修改内容后面再进行修正与理解。上一次只有一个三角形的考察,这次引入一个四边形,我便尝试去思考关于每个形状的共性,于是我创造一个GeometricObject的类,充当各种多边形的父类,因为我此时对于整体设计纯在缺陷,并不知道子类拥有那些公共函数,所以我仅仅将关于面积、边长等计算进行父类封装,并设计为抽象函数,让子类使用时重写,GeometricObject如下
public abstract class GeometricObject {
private java.util.Date dateCreated;
protected GeometricObject() {
dateCreated = new java.util.Date();
}
public abstract double getArea();
public abstract double getPerimeter();
public abstract int intersect(Line l);
}
对于Triangle类和Quariliteral类设计大致遵循上次blog的分析,仅仅只做了一点点修改,本次blog不做过多分析
Point类(点击以此展开)
class Point {
public double x = 0;
public double y = 0;
Point(double x, double y) {
this.x = x;
this.y = y;
}
public double pointDistance(Point n1) {
return Math.sqrt(Math.pow(this.x - n1.x, 2) + Math.pow(this.y - n1.y, 2));
}
}
Triangle类(点击以此展开)
public class Line {
private Point[] pointArr = new Point[2];
private double length = 0;
private double a=0, b=0, c=0;
public Line(Point n1, Point n2) {
if (n1.x == n2.x && n1.y == n2.y) {
System.out.println("points coincide");
System.exit(0);
}
pointArr[0] = n1;
pointArr[1] = n2;
this.length = n1.pointDistance(n2);
this.a = n1.y - n2.y;
this.b = n2.x - n1.x;
this.c = n1.x * n2.y - n2.x * n1.y;
}
public double slope() {
if (this.pointArr[0].x == this.pointArr[1].x) return 999;
else return -a / b;
}
public double VerticalDistance(Point n1) {
return (a * n1.x + b * n1.y + c )/ Math.sqrt(Math.pow(a, 2) + Math.pow(b, 2));
}
public boolean isCollinear(Point n1) {
return Math.abs(a * n1.x + b * n1.y + c )<0.000001;
}
public boolean isParallel(Line l1) {
return Math.abs(this.slope() - l1.slope())<0.000001;
}
public Point Intersection(Line l1) {
double det = l1.a * this.b - this.a * l1.b;
double x = (this.c * l1.b - l1.c * this.b) / det;
double y = (this.a * l1.c - l1.a * this.c) / det;
return new Point(x,y);
}
public boolean isInside(Point n1) {
return Math.abs(n1.pointDistance(pointArr[0]) + n1.pointDistance(pointArr[1]) - this.length) < 0.000001 && n1.pointDistance(pointArr[0]) != 0 && n1.pointDistance(pointArr[1]) != 0;
}
}
Triangle类(点击以此展开)
public class Triangle extends GeometricObject{
private final double[] len = new double[3];
private final Point[] pointArr = new Point[3];
private final Line[] lineArr = new Line[3];
public Triangle(Point n1, Point n2, Point n3) {
pointArr[0] = n1;
pointArr[1] = n2;
pointArr[2] = n3;
len[0] = n1.pointDistance(n2);
len[1] = n2.pointDistance(n3);
len[2] = n3.pointDistance(n1);
if (len[0] >= len[1] + len[2] || len[1] >= len[0] + len[2] || len[2] >= len[1] + len[0]) {
System.out.println("not a quadrilateral or triangle");
System.exit(0);
}
lineArr[0] = new Line(n1, n2);
lineArr[1] = new Line(n2, n3);
lineArr[2] = new Line(n3, n1);
}
public boolean Isosceles() {
return len[0] == len[1] || len[0] == len[2] || len[1] == len[2];
}
public boolean Equilateral() {
return len[0] == len[1] && len[1] == len[2];
}
public double getPerimeter() {
return len[0] + len[1] + len[2];
}
public double getArea() {
double Perimeter = getPerimeter();
return Math.sqrt(Perimeter / 2 * (Perimeter / 2 - len[0]) * (Perimeter / 2 - len[1]) * (Perimeter / 2 - len[2]));
}
public Point focus() {
double x = (pointArr[0].x + pointArr[1].x + pointArr[2].x) / 3;
double y = (pointArr[0].y + pointArr[1].y + pointArr[2].y) / 3;
return new Point(x, y);
}
public int triangularType() {
for (int i = 1; i < 3; i++) {
if (len[i] > len[0]) {
double temp = len[0];
len[0] = len[i];
len[i] = temp;
}
}
double num = Math.pow(len[1], 2) + Math.pow(len[2], 2) - Math.pow(len[0], 2);
if (num > 0.000001) return 1;
else if (num < -0.000001) return -1;
else return 0;
}
public int intersect(Line l) {
double[] temp = new double[3];
for (int i = 0; i < 3; i++) {
temp[i] = l.VerticalDistance(pointArr[(i + 1) % 3]) * l.VerticalDistance(pointArr[(i + 2) % 3]);
if (l.isCollinear(pointArr[i])) {
if (temp[i] == 0) return -1;
else if (temp[i] < 0) return 2;
else return 1;
}
}
if (temp[0] * temp[1] > 0 && temp[0] * temp[2] > 0) return 0;
else return 2;
}
public double[] partialArea(Line l) {
double[] partArea = new double[2];
Point[] p = new Point[2];
int n = 0;
double Height = 0;
for (int i = 0; i < 3; i++) {
if (l.VerticalDistance(pointArr[i]) * l.VerticalDistance(pointArr[(i + 1) % 3]) < 0) {
p[n++] = l.Intersection(lineArr[i]);
if (l.isCollinear(pointArr[(i + 2) % 3])) {
p[1] = pointArr[(i + 2) % 3];
Height = Math.abs(l.VerticalDistance(pointArr[i]));
break;
}
continue;
}
Height = Math.abs(l.VerticalDistance(pointArr[(i + 2) % 3]));
}
double botLength = p[0].pointDistance(p[1]);
partArea[0] = Math.min((Height * botLength) / 2, this.getArea() - (Height * botLength) / 2);
partArea[1] = Math.max((Height * botLength) / 2, this.getArea() - (Height * botLength) / 2);
return partArea;
}
public boolean isOnTriangle(Point n1) {
for (int i = 0; i < 3; i++) {
if (pointArr[i].pointDistance(n1) == 0) return true;
if (lineArr[i].isInside(n1)) return true;
}
return false;
}
public boolean isOut(Point n1) {
Line line = new Line(n1, pointArr[0]);
if (this.intersect(line) == 2) {
Point n = line.Intersection(lineArr[1]);
return (new Line(pointArr[0], n)).isInside(n1);
} else return false;
}
}
Quadrilateral类(点击以此展开)
class Quadrilateral extends GeometricObject {
private final double[] len = new double[4];
private final Point[] pointArr = new Point[4];
private final Line[] lineArr = new Line[4];
public Quadrilateral(Point n1, Point n2, Point n3, Point n4) {
pointArr[0] = n1;
pointArr[1] = n2;
pointArr[2] = n3;
pointArr[3] = n4;
len[0] = n1.pointDistance(n2);
len[1] = n2.pointDistance(n3);
len[2] = n3.pointDistance(n4);
len[3] = n4.pointDistance(n1);
lineArr[0] = new Line(n1, n2);
lineArr[1] = new Line(n2, n3);
lineArr[2] = new Line(n3, n4);
lineArr[3] = new Line(n4, n1);
}
public boolean isQuadrilateral() {
if (!lineArr[0].isParallel(lineArr[1]) && !lineArr[0].isParallel(lineArr[3]) && !lineArr[2].isParallel(lineArr[1]) && !lineArr[2].isParallel(lineArr[3])) {
if (!lineArr[1].isParallel(lineArr[3]) && lineArr[1].isInside(lineArr[1].Intersection(lineArr[3])) && lineArr[3].isInside(lineArr[1].Intersection(lineArr[3])))
return false;
else if (!lineArr[0].isParallel(lineArr[2]) && lineArr[0].isInside(lineArr[0].Intersection(lineArr[2])) && lineArr[2].isInside(lineArr[0].Intersection(lineArr[2])))
return false;
else return true;
}
return false;
}
public boolean isParallelogram() {
return this.isQuadrilateral() && lineArr[0].isParallel(lineArr[2]) && lineArr[1].isParallel(lineArr[3]);
}
public boolean isDiamond() {
return this.isQuadrilateral() && len[0] == len[1];
}
public boolean isRectangle() {
return this.isParallelogram() && new Triangle(pointArr[0], pointArr[1], pointArr[2]).triangularType() == 0;
}
public boolean isSquare() {
return this.isRectangle() && this.isDiamond();
}
public boolean isConvextriangle() {
for (int i = 0; i < 4; i++) {
if (lineArr[i].VerticalDistance(pointArr[(i + 2) % 4]) * lineArr[i].VerticalDistance(pointArr[(i + 3) % 4]) < 0)
return false;
}
return true;
}
public boolean isOnQuadrilateral(Point n1) {
for (int i = 0; i < 4; i++) {
if (pointArr[i].pointDistance(n1) == 0) return true;
if (lineArr[i].isInside(n1)) return true;
}
return false;
}
public boolean isOut(Point n1) {
double triangleAreaSum = 0;
for(int i = 0;i<4;i++){
if(!new Line(pointArr[i],pointArr[(i+1)%4]).isParallel(new Line(pointArr[i],n1)))
triangleAreaSum+=new Triangle(n1, pointArr[i], pointArr[(i+1)%4]).getArea();
}
return triangleAreaSum == getArea();
}
@Override
public int intersect(Line l) {
Line line = new Line(pointArr[1], pointArr[3]);
int intersect1 = new Triangle(pointArr[0], pointArr[1], pointArr[3]).intersect(l);
int intersect2 = new Triangle(pointArr[1], pointArr[2], pointArr[3]).intersect(l);
if (l.VerticalDistance(pointArr[1]) == 0 && l.VerticalDistance(pointArr[3]) == 0) return 2;
if (intersect1 == -1 || intersect2 == -1) return -1;
if (!l.isParallel(line) && line.isInside(line.Intersection(l))) {
intersect1 -= 1;
intersect2 -= 1;
}
if (l.VerticalDistance(pointArr[1]) == 0 || l.VerticalDistance(pointArr[3]) == 0) intersect2 -= 1;
return intersect1 + intersect2;
}
public double[] partialArea(Line l) {
double[] partArea = new double[2];
Point[] intersectPoint = new Point[4];
for (int i = 0; i < 4; i++) {
if (!l.isParallel(lineArr[i]) && ((lineArr[i].isInside(lineArr[i].Intersection(l))) || pointArr[i].pointDistance(lineArr[i].Intersection(l)) == 0))
intersectPoint[i] = lineArr[i].Intersection(l);
else
intersectPoint[i] = new Point(999, 999);
}
for (int i = 0; i < 4; i++) {
if (intersectPoint[i].x != 999 && intersectPoint[(i + 1) % 4].x != 999) {
partArea[1] = new Triangle(pointArr[(i + 1) % 4], lineArr[i].Intersection(l), lineArr[(i + 1) % 4].Intersection(l)).getArea();
break;
}
if (intersectPoint[i].x != 999 && intersectPoint[(i + 2) % 4].x != 999) {
if (l.isParallel(new Line(pointArr[i], pointArr[(i + 2) % 4]))) {
partArea[1] = this.getArea()*1/2;
break;
}
if (new Quadrilateral(pointArr[(i + 1) % 4], pointArr[(i + 2) % 4], intersectPoint[(i + 2) % 4], intersectPoint[i]).isQuadrilateral()) {
partArea[1] = new Quadrilateral(pointArr[(i + 1) % 4], pointArr[(i + 2) % 4], intersectPoint[i], intersectPoint[(i + 2) % 4]).getArea();
break;
}
}
}
partArea[0] = Math.min(partArea[1], this.getArea() - partArea[1]);
partArea[1] = Math.max(partArea[0], this.getArea() - partArea[0]);
return partArea;
}
@Override
public double getArea() {
Triangle triangle1 = new Triangle(pointArr[0], pointArr[1], pointArr[2]);
Triangle triangle2 = new Triangle(pointArr[1], pointArr[2], pointArr[3]);
Triangle triangle3 = new Triangle(pointArr[2], pointArr[3], pointArr[0]);
Triangle triangle4 = new Triangle(pointArr[3], pointArr[0], pointArr[1]);
return Math.min(triangle1.getArea() + triangle3.getArea(), triangle2.getArea() + triangle4.getArea());
}
@Override
public double getPerimeter() {
return len[0] + len[1] + len[2] + len[3];
}
}
上述内容很大程度是延续或者微微改造上一次作业的题目,在本道题目我并没有进行一个非常明确的重构,本道题目我主要聚焦于分析题目新的需求,这些需求主要体现在Judgement类和Main类上,先对Judgement进行分析
import java.math.RoundingMode;
import java.text.DecimalFormat;
public class Judgment {
private String arr = "";
public int value = 0;
public Point[] pointArr = new Point[20];
public Judgment(String str) {
String regex = "(\\d:)?([+|-]?(0|[1-9]\\d*)(\\.\\d+)?,[+|-]?(0|[1-9]\\d*)(\\.\\d+)? )+[+|-]?(0|[1-9]\\d*)(\\.\\d+)?,[+|-]?(0|[1-9]\\d*)(\\.\\d+)?( )?";
if (!str.matches(regex)) {
System.out.println("Wrong Format");
System.exit(0);
} else {
if (str.matches("(\\d:).*")) {
this.value = Integer.parseInt(str.split(":")[0]);
this.arr = str.split(":")[1];
} else this.arr = str;
int i = 0;
for (String point : arr.split(" ")) {
this.pointArr[i++] = new Point(Double.parseDouble(point.split(",")[0]),Double.parseDouble(point.split(",")[1]));
}
}
}
public void isBeyondNum(int n) {
if (arr.split(" ").length != n) {
System.out.println("wrong number of points");
System.exit(0);
}
}
public String PrintJudge(double x) {
DecimalFormat nf = new DecimalFormat("0.0##");
nf.setRoundingMode(RoundingMode.HALF_UP);
return nf.format(x);
}
}
核心的输入判定上并没什么区别,都是一些输入合法检测等,但是由于本道题目加入了一个要求:输出的数据若小数点后超过3位,只保留小数点后3位,多余部分采用四舍五入规则进到最低位。小数点后若不足3位,按原始位数显示,不必补齐。例如:1/3的结果按格式输出为 0.333,1.0按格式输出为1.0 所以加入一个PrintJudge的函数,这个函数可以对输入数据依据题目要求进行改造并返回结果字符串。对于Main函数先给代码如下
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
String arr = scanner.nextLine();
Judgment judgment = new Judgment(arr);
Point[] pointArr = judgment.pointArr;
Quadrilateral quadrilateral;
Line line;
switch (judgment.value) {
case 1:
judgment.isBeyondNum(4);
quadrilateral = new Quadrilateral(pointArr[0], pointArr[1], pointArr[2], pointArr[3]);
System.out.println(quadrilateral.isQuadrilateral() + " " + quadrilateral.isParallelogram());
break;
case 2:
judgment.isBeyondNum(4);
quadrilateral = new Quadrilateral(pointArr[0], pointArr[1], pointArr[2], pointArr[3]);
if (!quadrilateral.isQuadrilateral()) System.out.println("not a quadrilateral");
else
System.out.println(quadrilateral.isDiamond() + " " + quadrilateral.isRectangle() + " " + quadrilateral.isSquare());
break;
case 3:
judgment.isBeyondNum(4);
quadrilateral = new Quadrilateral(pointArr[0], pointArr[1], pointArr[2], pointArr[3]);
if (!quadrilateral.isQuadrilateral()) System.out.println("not a quadrilateral");
else System.out.println(quadrilateral.isConvextriangle() + " " + judgment.PrintJudge(quadrilateral.getPerimeter()) + " " + judgment.PrintJudge(quadrilateral.getArea()));
break;
case 4:
judgment.isBeyondNum(6);
line = new Line(pointArr[0], pointArr[1]);
for (int i = 0; i < 4; i++) {
if(pointArr[(i + 2) % 4 + 2].pointDistance(pointArr[i + 2]) == 0){
Triangle triangle = new Triangle(pointArr[i + 2], pointArr[(i + 1) % 4 + 2], pointArr[(i + 3) % 4 + 2]);
if (triangle.intersect(line) == -1)
System.out.println("The line is coincide with one of the lines");
else if (triangle.intersect(line) != 2) System.out.println(triangle.intersect(line));
else
System.out.println("2 " + judgment.PrintJudge(triangle.partialArea(line)[0]) + " " + judgment.PrintJudge(triangle.partialArea(line)[1]));
System.exit(0);
}
if (pointArr[(i + 1) % 4 + 2].pointDistance(pointArr[i + 2]) == 0 || pointArr[(i + 1) % 4 + 2].pointDistance(pointArr[(i + 2) % 4 + 2]) == 0 || new Line(pointArr[i + 2], pointArr[(i + 2) % 4 + 2]).isInside(pointArr[(i + 1) % 4 + 2])) {
Triangle triangle = new Triangle(pointArr[i + 2], pointArr[(i + 2) % 4 + 2], pointArr[(i + 3) % 4 + 2]);
if (triangle.intersect(line) == -1)
System.out.println("The line is coincide with one of the lines");
else if (triangle.intersect(line) != 2) System.out.println(triangle.intersect(line));
else
System.out.println("2 " + judgment.PrintJudge(triangle.partialArea(line)[0]) + " " + judgment.PrintJudge(triangle.partialArea(line)[1]));
System.exit(0);
}
}
quadrilateral = new Quadrilateral(pointArr[2], pointArr[3], pointArr[4], pointArr[5]);
if (quadrilateral.isQuadrilateral()) {
if (quadrilateral.intersect(line) == -1)
System.out.println("The line is coincide with one of the lines");
else if (quadrilateral.intersect(line) != 2)
System.out.println(quadrilateral.intersect(line));
else
System.out.println("2 " + judgment.PrintJudge(quadrilateral.partialArea(line)[0])+ " " +judgment.PrintJudge(quadrilateral.partialArea(line)[1]));
} else {
System.out.println("not a quadrilateral or triangle");
}
break;
case 5:
judgment.isBeyondNum(5);
line = new Line(pointArr[0], pointArr[1]);
for (int i = 0; i < 4; i++) {
if (pointArr[(i + 1) % 4 + 1].pointDistance(pointArr[i + 1]) == 0 || pointArr[(i + 1) % 4 + 1].pointDistance(pointArr[(i + 2) % 4 + 1]) == 0 || new Line(pointArr[i + 1], pointArr[(i + 2) % 4 + 1]).isInside(pointArr[(i + 1) % 4 + 1])) {
Triangle triangle = new Triangle(pointArr[i + 1], pointArr[(i + 2) % 4 + 1], pointArr[(i + 3) % 4 + 1]);
if (triangle.isOnTriangle(pointArr[0])) System.out.println("on the triangle");
else if(triangle.isOut(pointArr[0])) System.out.println("in the triangle");
else System.out.println("outof the triangle");
System.exit(0);
}
}
quadrilateral = new Quadrilateral(pointArr[1], pointArr[2], pointArr[3], pointArr[4]);
if (quadrilateral.isQuadrilateral()) {
if (quadrilateral.isOnQuadrilateral(pointArr[0])) System.out.println("on the quadrilateral");
else if(quadrilateral.isOut(pointArr[0])) System.out.println("in the quadrilateral");
else System.out.println("outof the quadrilateral");
System.exit(0);
} else {
System.out.println("not a quadrilateral or triangle");
}
break;
default:
System.out.println("Wrong Format");
}
}
}
前面三道case的选项都是基础的题目体现,因为题目分析并没有做到足够的具体与,客观后面题目也对此进行了改造,所以便不做此处分析,后面两道Case,我使用了循环去便利点线关系来判断形状,考虑情况较为复杂也较为凌乱,从SourceMonitor的生成报表内容可以看出

生成的报表显示最大复杂度严重超标,在本次实验下一道题目上也有体现,可能是由于题目过多的高幂循环导致,对此我也在后续的改进建议中对出现的这个问题进行了改进,因为题目多Point等类直接使用了上一道题目的,所以有还能多无用的函数也在其中,导致代码看起来有一些复杂,若是删除某些无用函数,可让代码稍微简洁一点,因为大致类信息,下述题目也要用到,所以PowerDesigner的相应类图后续一同给出。
7-1 点线形系列5-凸五边形的计算-1
本道题目在上一道题目基础上加入了五边形类,题目要求如下
用户输入一组选项和数据,进行与五边形有关的计算。
以下五边形顶点的坐标要求按顺序依次输入,连续输入的两个顶点是相邻顶点,第一个和最后一个输入的顶点相邻。
选项包括:
1:输入五个点坐标,判断是否是五边形,判断结果输出true/false。
2:输入五个点坐标,判断是凹五边形(false)还是凸五边形(true),如果是凸五边形,则再输出五边形周长、面积,结果之间以一个英文空格符分隔。 若五个点坐标无法构成五边形,输出"not a pentagon"
3:输入七个点坐标,前两个点构成一条直线,后五个点构成一个凸五边形、凸四边形或凸三角形,输出直线与五边形、四边形或三角形相交的交点数量。如果交点有两个,再按面积从小到大输出被直线分割成两部分的面积(不换行)。若直线与多边形形的一条边线重合,输出"The line is coincide with one of the lines"。若后五个点不符合五边形输入,若前两点重合,输出"points coincide"。
以上3选项中,若输入的点无法构成多边形,则输出"not a polygon"。输入的五个点坐标可能存在冗余,假设多边形一条边上两个端点分别是x、y,边线中间有一点z,另一顶点s:
1)符合要求的输入:顶点重复或者z与xy都相邻,如:x x y s、x z y s、x y x s、s x y y。此时去除冗余点,保留一个x、一个y。
2) 不符合要求的输入:z不与xy都相邻,如:z x y s、x z s y、x s z y
前两问基本和之前一样换汤不换药,对于第第三问和上道题目存在共同点,但大致思路无二,关于GeometricObject类、Point类、Line类、Triangle类与Quadrilatera类基本相同,便不予展示,对于新加入的Penagon类如下展示
public class Pentagon extends GeometricObject {
private final double[] len = new double[5];
private final Point[] pointArr = new Point[5];
private final Line[] lineArr = new Line[5];
public Pentagon(Point n1, Point n2, Point n3, Point n4, Point n5) {
pointArr[0] = n1;
pointArr[1] = n2;
pointArr[2] = n3;
pointArr[3] = n4;
pointArr[4] = n5;
len[0] = n1.pointDistance(n2);
len[1] = n2.pointDistance(n3);
len[2] = n3.pointDistance(n4);
len[3] = n4.pointDistance(n5);
len[4] = n5.pointDistance(n1);
lineArr[0] = new Line(n1, n2);
lineArr[1] = new Line(n2, n3);
lineArr[2] = new Line(n3, n4);
lineArr[3] = new Line(n4, n5);
lineArr[4] = new Line(n5, n1);
}
public boolean isPentagon() {
for (int i = 0; i < 5; i++) {
for (int j = 1; j < 5; j++) {
if (pointArr[i].pointDistance(pointArr[(i + j) % 5]) == 0) return false;
if (!lineArr[i].isParallel(lineArr[(i + j) % 5]) && lineArr[i].isInside(lineArr[i].Intersection(lineArr[(i + j) % 5])) && lineArr[(i + j) % 5].isInside(lineArr[i].Intersection(lineArr[(i + j) % 5])))
return false;
}
if (lineArr[i].isParallel(lineArr[(i + 1) % 5])) return false;
}
return true;
}
public boolean isConvexPentagon() {
for (int i = 0; i < 5; i++) {
if (!new Quadrilateral(pointArr[(i + 1) % 5], pointArr[(i + 2) % 5], pointArr[(i + 3) % 5], pointArr[(i + 4) % 5]).isQuadrilateral())
return false;
if (new Quadrilateral(pointArr[(i + 1) % 5], pointArr[(i + 2) % 5], pointArr[(i + 3) % 5], pointArr[(i + 4) % 5]).isOut(pointArr[i]))
return false;
}
return true;
}
@Override
public double getArea() {
if (isConvexPentagon()) {
return new Triangle(pointArr[0], pointArr[1], pointArr[2]).getArea() + new Quadrilateral(pointArr[2], pointArr[3], pointArr[4], pointArr[0]).getArea();
} else {
for (int i = 0; i < 5; i++) {
if (new Quadrilateral(pointArr[(i + 1) % 5], pointArr[(i + 2) % 5], pointArr[(i + 3) % 5], pointArr[(i + 4) % 5]).isOut(pointArr[i]))
return new Quadrilateral(pointArr[(i + 1) % 5], pointArr[(i + 2) % 5], pointArr[(i + 3) % 5], pointArr[(i + 4) % 5]).getArea() - new Triangle(pointArr[i], pointArr[(i + 1) % 5], pointArr[(i + 4) % 5]).getArea();
}
}
return 0;
}
@Override
public double getPerimeter() {
return len[0] + len[1] + len[2] + len[3] + len[4];
}
@Override
public int intersect(Line l) {
double[] temp = new double[5];
for (int i = 0; i < 5; i++) {
temp[i] = l.VerticalDistance(pointArr[(i + 1) % 5]) * l.VerticalDistance(pointArr[(i + 4) % 5]);
if (l.isCollinear(pointArr[i])) {
if (temp[i] == 0) return -1;
else if (temp[i] < 0) return 2;
else return 1;
}
}
if (temp[0] * temp[1] > 0 && temp[0] * temp[2] > 0 && temp[0] * temp[3] > 0 && temp[0] * temp[4] > 0) return 0;
else return 2;
}
public double[] partialArea(Line l) {
double[] partArea = new double[2];
Point[] intersectPoint = new Point[5];
for (int i = 0; i < 5; i++) {
if (!l.isParallel(lineArr[i]) && ((lineArr[i].isInside(lineArr[i].Intersection(l))) || pointArr[i].pointDistance(lineArr[i].Intersection(l)) == 0))
intersectPoint[i] = lineArr[i].Intersection(l);
else
intersectPoint[i] = new Point(999, 999);
}
for (int i = 0; i < 5; i++) {
if (intersectPoint[i].x != 999 && (intersectPoint[(i + 1) % 5].x != 999 || pointArr[(i + 2) % 5].pointDistance(intersectPoint[(i + 2) % 5]) == 0)) {
intersectPoint[(i + 1) % 5] = pointArr[(i + 2) % 5];
partArea[1] = new Triangle(pointArr[(i + 1) % 5], intersectPoint[i], intersectPoint[(i + 1) % 5]).getArea();
break;
}
if (intersectPoint[i].x != 999 && intersectPoint[(i + 2) % 5].x != 999) {
if (new Quadrilateral(pointArr[(i + 1) % 5], pointArr[(i + 2) % 5], intersectPoint[(i + 2) % 5], intersectPoint[i]).isQuadrilateral()) {
partArea[1] = new Quadrilateral(pointArr[(i + 1) % 5], pointArr[(i + 2) % 5], intersectPoint[(i + 2) % 5], intersectPoint[i]).getArea();
break;
}
}
}
partArea[0] = Math.min(partArea[1], this.getArea() - partArea[1]);
partArea[1] = Math.max(partArea[0], this.getArea() - partArea[0]);
return partArea;
}
}
对于面积等的计算方式后来我觉得非常糟糕,也进行了改进,Pentagon类和其他图像类设计思路一致,对于Judgment类,需要注意题目要求的更改输出的数据若小数点后超过3位,只保留小数点后3位
即可,其他并无需改动,Main类如下
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
String arr = scanner.nextLine();
Judgment judgment = new Judgment(arr);
Point[] pointArr = judgment.pointArr;
Pentagon pentagons;
switch (judgment.value) {
case 1:
judgment.isBeyondNum(5);
pentagons = new Pentagon(pointArr[0], pointArr[1], pointArr[2], pointArr[3], pointArr[4]);
System.out.println(pentagons.isPentagon());
break;
case 2:
judgment.isBeyondNum(5);
pentagons = new Pentagon(pointArr[0], pointArr[1], pointArr[2], pointArr[3], pointArr[4]);
if (!pentagons.isPentagon()) System.out.println("not a pentagon");
else if (pentagons.isConvexPentagon())
System.out.println(pentagons.isConvexPentagon() + " " + judgment.PrintJudge(pentagons.getPerimeter()) + " " + judgment.PrintJudge(pentagons.getArea()));
else System.out.println(false);
break;
case 3:
judgment.isBeyondNum(7);
Line line = new Line(pointArr[0], pointArr[1]);
line.isLine();
for (int i = 0; i < 5; i++) {
Triangle triangle = new Triangle(pointArr[i + 2], pointArr[(i + 3) % 5 + 2], pointArr[(i + 4) % 5 + 2]);
if(new Line(pointArr[i + 2], pointArr[(i + 3) % 5 + 2]).isInside(pointArr[(i + 1) % 5 + 2])&&new Line(pointArr[i + 2], pointArr[(i + 3) % 5 + 2]).isInside(pointArr[(i + 2) % 5 + 2])){
if (triangle.isTriangle()) {
if (triangle.intersect(line) == -1)
System.out.println("The line is coincide with one of the lines");
else if (triangle.intersect(line) != 2) System.out.println(triangle.intersect(line));
else
System.out.println("2 " + judgment.PrintJudge(triangle.partialArea(line)[0]) + " " + judgment.PrintJudge(triangle.partialArea(line)[1]));
System.exit(0);
}
}
triangle = new Triangle(pointArr[(i + 2) % 5 + 2], pointArr[(i + 3) % 5 + 2], pointArr[(i + 4) % 5 + 2]);
if (pointArr[(i + 1) % 5 + 2].pointDistance(pointArr[(i + 3) % 5 + 2]) == 0 &&(pointArr[(i + 2) % 5 + 2].pointDistance(pointArr[i + 2]) == 0 || pointArr[(i + 4) % 5 + 2].pointDistance(pointArr[i + 2]) == 0 ||
new Line(pointArr[(i + 2) % 5 + 2], pointArr[(i + 4) % 5 + 2]).isInside(pointArr[i + 2]))) {
if (triangle.isTriangle()) {
if (triangle.intersect(line) == -1)
System.out.println("The line is coincide with one of the lines");
else if (triangle.intersect(line) != 2) System.out.println(triangle.intersect(line));
else
System.out.println("2 " + judgment.PrintJudge(triangle.partialArea(line)[0]) + " " + judgment.PrintJudge(triangle.partialArea(line)[1]));
System.exit(0);
}
}
triangle = new Triangle(pointArr[(i + 1) % 5 + 2], pointArr[(i + 4) % 5 + 2], pointArr[i + 2]);
if (pointArr[i + 2].pointDistance(pointArr[(i + 2) % 5 + 2]) == 0 && (pointArr[(i + 2) % 5 + 2].pointDistance(pointArr[(i + 3) % 5 + 2]) == 0 || pointArr[(i + 4) % 5 + 2].pointDistance(pointArr[(i + 3) % 5 + 2]) == 0 ||
new Line(pointArr[(i + 2) % 5 + 2], pointArr[(i + 4) % 5 + 2]).isInside(pointArr[(i + 3) % 5 + 2]))) {
if (triangle.isTriangle()) {
if (triangle.intersect(line) == -1)
System.out.println("The line is coincide with one of the lines");
else if (triangle.intersect(line) != 2) System.out.println(triangle.intersect(line));
else
System.out.println("2 " + judgment.PrintJudge(triangle.partialArea(line)[0]) + " " + judgment.PrintJudge(triangle.partialArea(line)[1]));
System.exit(0);
}
}
triangle = new Triangle(pointArr[(i + 1) % 5 + 2], pointArr[(i + 3) % 5 + 2], pointArr[(i + 4) % 5 + 2]);
if ((pointArr[(i + 1) % 5 + 2].pointDistance(pointArr[i + 2]) == 0 || pointArr[(i + 4) % 5 + 2].pointDistance(pointArr[i + 2]) == 0 ||
new Line(pointArr[(i + 1) % 5 + 2], pointArr[(i + 4) % 5 + 2]).isInside(pointArr[i + 2])) &&
(pointArr[(i + 1) % 5 + 2].pointDistance(pointArr[(i + 2) % 5 + 2]) == 0 || pointArr[(i + 3) % 5 + 2].pointDistance(pointArr[(i + 2) % 5 + 2]) == 0 ||
new Line(pointArr[(i + 1) % 5 + 2], pointArr[(i + 3) % 5 + 2]).isInside(pointArr[(i + 2) % 5 + 2]))) {
if (triangle.isTriangle()) {
if (triangle.intersect(line) == -1)
System.out.println("The line is coincide with one of the lines");
else if (triangle.intersect(line) != 2) System.out.println(triangle.intersect(line));
else
System.out.println("2 " + judgment.PrintJudge(triangle.partialArea(line)[0]) + " " + judgment.PrintJudge(triangle.partialArea(line)[1]));
System.exit(0);
}
}
if (new Quadrilateral(pointArr[(i + 1) % 5 + 2], pointArr[(i + 2) % 5 + 2], pointArr[(i + 3) % 5 + 2], pointArr[(i + 4) % 5 + 2]).isQuadrilateral()) {
if (pointArr[(i + 2) % 5 + 2].pointDistance(pointArr[i + 2]) == 0 || pointArr[(i + 1) % 5 + 2].pointDistance(pointArr[i + 2]) == 0 || pointArr[(i + 4) % 5 + 2].pointDistance(pointArr[i + 2]) == 0 ||
new Line(pointArr[(i + 1) % 5 + 2], pointArr[(i + 4) % 5 + 2]).isInside(pointArr[i + 2])) {
Quadrilateral quadrilateral = new Quadrilateral(pointArr[(i + 1) % 5 + 2], pointArr[(i + 2) % 5 + 2], pointArr[(i + 3) % 5 + 2], pointArr[(i + 4) % 5 + 2]);
if (quadrilateral.intersect(line) == -1)
System.out.println("The line is coincide with one of the lines");
else if (quadrilateral.intersect(line) != 2)
System.out.println(quadrilateral.intersect(line));
else
System.out.println("2 " + judgment.PrintJudge(quadrilateral.partialArea(line)[0]) + " " + judgment.PrintJudge(quadrilateral.partialArea(line)[1]));
System.exit(0);
}
}
}
pentagons = new Pentagon(pointArr[2], pointArr[3], pointArr[4], pointArr[5], pointArr[6]);
if (pentagons.isPentagon()) {
if (pentagons.intersect(line) == -1)
System.out.println("The line is coincide with one of the lines");
else if (pentagons.intersect(line) != 2)
System.out.println(pentagons.intersect(line));
else
System.out.println("2 " + judgment.PrintJudge(pentagons.partialArea(line)[0]) + " " + judgment.PrintJudge(pentagons.partialArea(line)[1]));
} else System.out.println("not a polygon");
break;
default:
System.out.println("Wrong Format");
}
}
}
对于本道题目可供分析的其实不多,虽然题目完成,但是在复杂度等方面有着很大的问题,SourceMonitor的生成报表内容也可以看出和上面一道题目有着相同的复杂度过大的问题,在添加一个Pantegon类导致问题不仅没有改善,反而更加严重。

7-2 点线形系列5-凸五边形的计算-2
这道题目与之前题目的思路有着较大改变,主要是因为继续沿用之前的思路难以去解决该类题目,导致不得不去思考其他方案,题目要求如下
用户输入一组选项和数据,进行与五边形有关的计算。
以下五边形顶点的坐标要求按顺序依次输入,连续输入的两个顶点是相邻顶点,第一个和最后一个输入的顶点相邻。
选项包括:
4:输入十个点坐标,前、后五个点分别构成一个凸多边形(三角形、四边形、五边形),判断它们两个之间是否存在包含关系(一个多边形有一条或多条边与另一个多边形重合,其他部分都包含在另一个多边形内部,也算包含)。
两者存在六种关系:1、分离(完全无重合点) 2、连接(只有一个点或一条边重合) 3、完全重合 4、被包含(前一个多边形在后一个多边形的内部)5、交错 6、包含(后一个多边形在前一个多边形的内部)。
各种关系的输出格式如下:
1、no overlapping area between the previous triangle/quadrilateral/ pentagon and the following triangle/quadrilateral/ pentagon
2、the previous triangle/quadrilateral/ pentagon is connected to the following triangle/quadrilateral/ pentagon
3、the previous triangle/quadrilateral/ pentagon coincides with the following triangle/quadrilateral/ pentagon
4、the previous triangle/quadrilateral/ pentagon is inside the following triangle/quadrilateral/ pentagon
5、the previous triangle/quadrilateral/ pentagon is interlaced with the following triangle/quadrilateral/ pentagon
6、the previous triangle/quadrilateral/ pentagon contains the following triangle/quadrilateral/ pentagon
5:输入十个点坐标,前、后五个点分别构成一个凸多边形(三角形、四边形、五边形),输出两个多边形公共区域的面积。注:只考虑每个多边形被另一个多边形分割成最多两个部分的情况,不考虑一个多边形将另一个分割成超过两个区域的情况。
6:输入六个点坐标,输出第一个是否在后五个点所构成的多边形(限定为凸多边形,不考虑凹多边形),的内部(若是五边形输出in the pentagon/outof the pentagon,若是四边形输出in the quadrilateral/outof the quadrilateral,若是三角形输出in the triangle/outof the triangle)。输入入错存在冗余点要排除,冗余点的判定方法见选项5。如果点在多边形的某条边上,输出"on the triangle/on the quadrilateral/on the pentagon"。
以上4、5、6选项输入的五个点坐标可能存在冗余,假设多边形一条边上两个端点分别是x、y,边线中间有一点z,另一顶点s:
1)符合要求的输入:顶点重复或者z与xy都相邻,如:x x y s、x z y s、x y x s、s x y y。此时去除冗余点,保留一个x、一个y。
2) 不符合要求的输入:z不与xy都相邻,如:z x y s、x z s y、x s z y
本题需要判断十个点之间,每五个组成的关系,对于每五个点的形状判断再放到Main类的主函数李就显的过于复杂,并没有完美的使用类该有的复用性,所以需要对GeometricObject进行改造,先将改造后GeometricObject贴出,之后我们再做分析
public abstract class GeometricObject {
private java.util.Date dateCreated;
protected Point[] pointArr;
protected Line[] lineArr;
public String Shape = "polygon";
public GeometricObject(String Shape) {
this.Shape = Shape;
}
public static GeometricObject JudgeShape(Point[] pointArr) {
for (int i = 0; i < 5; i++) {
Triangle triangle = new Triangle(pointArr[i], pointArr[(i + 3) % 5], pointArr[(i + 4) % 5]);
if (new Line(pointArr[i], pointArr[(i + 3) % 5]).isInside(pointArr[(i + 1) % 5]) && new Line(pointArr[i], pointArr[(i + 3) % 5]).isInside(pointArr[(i + 2) % 5])) {
if (triangle.isTriangle()) {
return triangle;
}
}
triangle = new Triangle(pointArr[(i + 2) % 5], pointArr[(i + 3) % 5], pointArr[(i + 4) % 5]);
if (pointArr[(i + 1) % 5].pointDistance(pointArr[(i + 3) % 5]) == 0 && (pointArr[(i + 2) % 5].pointDistance(pointArr[i]) == 0 || pointArr[(i + 4) % 5].pointDistance(pointArr[i]) == 0 ||
new Line(pointArr[(i + 2) % 5], pointArr[(i + 4) % 5]).isInside(pointArr[i]))) {
if (triangle.isTriangle()) {
return triangle;
}
}
triangle = new Triangle(pointArr[(i + 1) % 5], pointArr[(i + 4) % 5], pointArr[i]);
if (pointArr[i].pointDistance(pointArr[(i + 2) % 5]) == 0 && (pointArr[(i + 2) % 5].pointDistance(pointArr[(i + 3) % 5]) == 0 || pointArr[(i + 4) % 5].pointDistance(pointArr[(i + 3) % 5]) == 0 ||
new Line(pointArr[(i + 2) % 5], pointArr[(i + 4) % 5]).isInside(pointArr[(i + 3) % 5]))) {
if (triangle.isTriangle()) {
return triangle;
}
}
triangle = new Triangle(pointArr[(i + 1) % 5], pointArr[(i + 3) % 5], pointArr[(i + 4) % 5]);
if ((pointArr[(i + 1) % 5].pointDistance(pointArr[i]) == 0 || pointArr[(i + 4) % 5].pointDistance(pointArr[i]) == 0 ||
new Line(pointArr[(i + 1) % 5], pointArr[(i + 4) % 5]).isInside(pointArr[i])) &&
(pointArr[(i + 1) % 5].pointDistance(pointArr[(i + 2) % 5]) == 0 || pointArr[(i + 3) % 5].pointDistance(pointArr[(i + 2) % 5]) == 0 ||
new Line(pointArr[(i + 1) % 5], pointArr[(i + 3) % 5]).isInside(pointArr[(i + 2) % 5]))) {
if (triangle.isTriangle()) {
return triangle;
}
}
if (new Quadrilateral(pointArr[(i + 1) % 5], pointArr[(i + 2) % 5], pointArr[(i + 3) % 5], pointArr[(i + 4) % 5]).isQuadrilateral()) {
if (pointArr[(i + 2) % 5].pointDistance(pointArr[i]) == 0 || pointArr[(i + 1) % 5].pointDistance(pointArr[i]) == 0 || pointArr[(i + 4) % 5].pointDistance(pointArr[i]) == 0 || pointArr[(i + 3) % 5].pointDistance(pointArr[i]) == 0 ||
new Line(pointArr[(i + 1) % 5], pointArr[(i + 4) % 5]).isInside(pointArr[i])) {
return new Quadrilateral(pointArr[(i + 1) % 5], pointArr[(i + 2) % 5], pointArr[(i + 3) % 5], pointArr[(i + 4) % 5]);
}
}
}
Pentagon pentagons = new Pentagon(pointArr[0], pointArr[1], pointArr[2], pointArr[3], pointArr[4]);
if (pentagons.isPentagon()) {
return pentagons;
} else {
System.out.println("not a polygon");
System.exit(0);
}
return pentagons;
}
public final int GraphicalRelationship(GeometricObject Shape) {
int temp1 = 0, temp2 = 0;
boolean isIn1 = false, isIn2 = false;
for (Point point : pointArr) {
if (Shape.isOnGeometricObject(point)) temp1 += 0;
else if (Shape.isOut(point)) {
temp1 += 1;
isIn1 = true;
} else temp1 += 6;
}
for (Point point : Shape.pointArr) {
if (isOnGeometricObject(point))
temp2 += 0;
else if (isOut(point)) {
temp2 += 1;
isIn2 = true;
} else temp2 += 6;
}
if (temp1 == pointArr.length * 6 && temp2 == Shape.pointArr.length * 6) return 1;
else if (temp1 == 0 && temp2 == 0 && getArea() == Shape.getArea()) return 3;
else if (temp1 <= 5 && temp2 >= 6 && getArea() < Shape.getArea()) return 4;
else if (temp2 <= 5 && temp1 >= 6 && getArea() > Shape.getArea()) return 6;
else if (!isIn1 && !isIn2 && isConnected(Shape)) return 2;
else return 5;
}
private boolean isConnected(GeometricObject Shape) {
for (int i = 0; i < lineArr.length; i++) {
for (double j = 0.25; j < 1; j += 0.25) {
Point point = new Point(pointArr[i].x - (pointArr[i].x - pointArr[(i + 1) % lineArr.length].x) * j, pointArr[i].y - (pointArr[i].y - pointArr[(i + 1) % lineArr.length].y) * j);
if (Shape.isOut(point) && !Shape.isOnGeometricObject(point)) return false;
}
}
return true;
}
//5:0,0 0,0 4,0 4,4 0,4 2,2 4,1 4,-1 -1,-1 -1,0
public final Point[] getCrossObject(GeometricObject Shape) {
Point[] crossPoint = new Point[10];
int temp = 0;
for (int i = 0; i < lineArr.length; i++) {
if (Shape.isOut(pointArr[i]) && !Shape.isOnGeometricObject(pointArr[i])) crossPoint[temp++] = pointArr[i];
for (int j = 0; j < Shape.lineArr.length; j++) {
if (Shape.lineArr[j].isParallel(lineArr[i])) {
if (Shape.lineArr[j].isInside(pointArr[i])) crossPoint[temp++] = pointArr[i];
if (lineArr[i].isInside(Shape.pointArr[j])) crossPoint[temp++] = Shape.pointArr[j];
if (Shape.pointArr[j].pointDistance(pointArr[i]) == 0) crossPoint[temp++] = Shape.pointArr[j];
continue;
}
Point point = Shape.lineArr[j].Intersection(lineArr[i]);
if ((Shape.lineArr[j].isInside(point) || point.pointDistance(Shape.pointArr[j]) == 0)
&& (lineArr[i].isInside(point) || point.pointDistance(pointArr[i]) == 0)) {
crossPoint[temp++] = point;
}
}
}
return java.util.Arrays.copyOf(crossPoint,temp);
}
public final void getPointArr(Point[] pointArr, Line[] lineArr) {
this.pointArr = pointArr;
this.lineArr = lineArr;
}
public double getArea() {
double sum = 0;
for (int i = 0; i < pointArr.length; i++) {
sum += 0.5 * (pointArr[i].x * pointArr[(i + 1) % pointArr.length].y - pointArr[i].y * pointArr[(i + 1) % pointArr.length].x);
}
return Math.abs(sum);
}
public abstract double getPerimeter();
public abstract int intersect(Line l);
public abstract boolean isOnGeometricObject(Point point);
public abstract boolean isOut(Point point);
}
GeometricObject继续沿用上一次的抽象方法,对Triangle类、Quadrilateral类以及Pentagon类进行集合约束,但也加入了几个不可基础的private方法,有静态方法的JudgeShape用于判断传入的五个点的形状,这样就可以通过在Main方法中调用实现多次判断的复用,也有GraphicalRelationship方法去判断传入多变形与该多边形之间的关系,通过之前实现的点在图像内或在图像外,可以简单的判断1.分离 3.完全重合 4.被包含 6.包含几个关系,对于交错的判断过于复杂,可以将它放入else的判断,这样只需要判断正确2.连接即可,对于连接判断我选择了一个较为取巧的方法,虽然存在一些明显问题,但如果是为了解题的情况下我觉得不失为一直明智的方法,通过对每条边进行点等分,判断几个等分点是否在另一个图像内部来判断所在直线是否在另一个图像内部,本道题目我选择了三等分,通过线段关系来判断是否是连接。下一问我思考了很久,后来在浏览查询资料的情况下知道了 鞋带定理:也叫高斯面积公式,是一种数学算法,可求确定区域的一个简单多边形的面积。该多边形是由它们顶点描述笛卡尔坐标中的平面。用户交叉相乘相应的坐标以找到包围该多边形的区域,并从周围的多边形中减去该区域以找到其中的多边形的区域。之所以称为鞋带公式,是因为对构成多边形的坐标进行恒定的交叉乘积,就像系鞋带一样。


使用该公式可以解决任意多边形面积问题,这也为我后面对整体进行改进留下理论空间,除此之外,我还运用了向量知识对点进行重写排序,相关算法函数我写在Judgment类,如下
import java.math.RoundingMode;
import java.text.DecimalFormat;
public class Judgment {
private String arr = "";
public int value = 0;
public Point[] pointArr = new Point[10];
public Judgment(String str) {
String regex = "(\\d:)?([+|-]?(0|[1-9]\\d*)(\\.\\d+)?,[+|-]?(0|[1-9]\\d*)(\\.\\d+)? )+[+|-]?(0|[1-9]\\d*)(\\.\\d+)?,[+|-]?(0|[1-9]\\d*)(\\.\\d+)?( )?";
if (!str.matches(regex)) {
System.out.println("Wrong Format");
System.exit(0);
} else {
if (str.matches("(\\d:).*")) {
this.value = Integer.parseInt(str.split(":")[0]);
this.arr = str.split(":")[1];
} else this.arr = str;
int i = 0;
for (String point : arr.split(" ")) {
this.pointArr[i++] = new Point(Double.parseDouble(point.split(",")[0]), Double.parseDouble(point.split(",")[1]));
}
}
}
public void isBeyondNum(int n) {
if (arr.split(" ").length != n) {
System.out.println("wrong number of points");
System.exit(0);
}
}
public static String PrintJudge(double x) {
DecimalFormat nf = new DecimalFormat("0.0##");
nf.setRoundingMode(RoundingMode.HALF_UP);
return nf.format(x);
}
private static boolean isVector(Point o, Point a, Point b) {
if (a.x >= 0 && b.x < 0)
return true;
if (a.x == 0 && b.x == 0)
return a.y > b.y;
Point n1 = new Point(a.x - o.x, a.y - o.y);
Point n2 = new Point(b.x - o.x, b.y - o.y);
if (n1.x * n2.y == n2.x * n1.y) {
return a.pointDistance(o) > b.pointDistance(o);
}
return n1.x * n2.y - n2.x * n1.y > 0;
}
public static Point[] ArrayDeformation(Point[] PointArr) {
int temp = 0;
Point[] crossPoint = new Point[PointArr.length];
for (Point point : PointArr) {
int flag = 0;
for (int j = 0; j < temp; j++) {
if (point.pointDistance(crossPoint[j]) == 0) flag = 1;
}
if (flag == 0) crossPoint[temp++] = point;
}
double nx = 0, ny = 0;
for (int i = 0; i < temp; i++) {
nx += crossPoint[i].x;
ny += crossPoint[i].y;
}
Point o = new Point(nx / temp, ny / temp), t = null;
for (int i = 0; i < temp - 1; i++) {
for (int j = 0; j < temp - i - 1; j++) {
if (Judgment.isVector(o, crossPoint[j], crossPoint[j + 1])) {
t = crossPoint[(j) % temp];
crossPoint[(j) % temp] = crossPoint[(j + 1) % temp];
crossPoint[(j + 1) % temp] = t;
}
}
}
return java.util.Arrays.copyOf(crossPoint,temp);
}
public static double getArea(Point[] pointArr) {
double sum = 0;
for (int i = 0; i < pointArr.length; i++) {
sum += 0.5 * (pointArr[i].x * pointArr[(i + 1) % pointArr.length].y - pointArr[i].y * pointArr[(i + 1) % pointArr.length].x);
}
return Math.abs(sum);
}
}
判断图像得出重心,通过每个点与重心进行比较可以得出每个点与重心大致的位置关系,以此可以推测出任何凸多边形的向量点顺或者逆时针点排列,Judgment类中静态方法isVector即是判断点与点的向量位置关系,而静态方法ArrayDeFormation便是对坐标点进行重排列,以此思路去完成第二问的交叉面积,即可使用先存储两个多变形全部交点,再去重和重排列,最后根据得到的点使用鞋带定理算出相应面积,关于Point类、Line类、Triangle类、Quadrilatera类与Pentagon类基本相同,下述展示该题Main方法
import PTA.Graphic.*;
import java.util.Scanner;
import static PTA.Graphic.Judgment.PrintJudge;
public class HW2 {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
String arr = scanner.nextLine();
Judgment judgment = new Judgment(arr);
Point[] pointArr = judgment.pointArr;
Point[] pointArr1 = new Point[5];
Point[] pointArr2 = new Point[5];
GeometricObject Shape1;
GeometricObject Shape2;
switch (judgment.value) {
case 4:
judgment.isBeyondNum(10);
System.arraycopy(pointArr, 0, pointArr1, 0, 5);
Shape1 = GeometricObject.JudgeShape(pointArr1);
System.arraycopy(pointArr, 5, pointArr2, 0, 5);
Shape2 = GeometricObject.JudgeShape(pointArr2);
if (Shape1.GraphicalRelationship(Shape2) == 1)
System.out.println("no overlapping area between the previous " + Shape1.Shape + " and the following " + Shape2.Shape);
else if (Shape1.GraphicalRelationship(Shape2) == 2)
System.out.println("the previous " + Shape1.Shape + " is connected to the following " + Shape2.Shape);
else if (Shape1.GraphicalRelationship(Shape2) == 3)
System.out.println("the previous " + Shape1.Shape + " coincides with the following " + Shape2.Shape);
else if (Shape1.GraphicalRelationship(Shape2) == 4)
System.out.println("the previous " + Shape1.Shape + " is inside the following " + Shape2.Shape);
else if (Shape1.GraphicalRelationship(Shape2) == 5)
System.out.println("the previous " + Shape1.Shape + " is interlaced with the following " + Shape2.Shape);
else
System.out.println("the previous " + Shape1.Shape + " contains the following " + Shape2.Shape);
break;
case 5:
judgment.isBeyondNum(10);
System.arraycopy(pointArr, 0, pointArr1, 0, 5);
Shape1 = GeometricObject.JudgeShape(pointArr1);
System.arraycopy(pointArr, 5, pointArr2, 0, 5);
Shape2 = GeometricObject.JudgeShape(pointArr2);
Point[] crossPoint1 = Shape1.getCrossObject(Shape2);
Point[] crossPoint2 = Shape2.getCrossObject(Shape1);
Point[] crossPoint = new Point[crossPoint1.length + crossPoint2.length];
System.arraycopy(crossPoint1, 0, crossPoint, 0, crossPoint1.length);
System.arraycopy(crossPoint2, 0, crossPoint, crossPoint1.length, crossPoint2.length);
System.out.println(Judgment.PrintJudge(Judgment.getArea(Judgment.ArrayDeformation(crossPoint))));
break;
case 6:
judgment.isBeyondNum(6);
System.arraycopy(pointArr, 1, pointArr1, 0, 5);
Shape1 = GeometricObject.JudgeShape(pointArr1);
if (Shape1.isOnGeometricObject(pointArr[0])) System.out.println("on the " + Shape1.Shape);
else if (Shape1.isOut(pointArr[0])) System.out.println("in the " + Shape1.Shape);
else System.out.println("outof the " + Shape1.Shape);
System.exit(0);
break;
default:
System.out.println("Wrong Format");
}
}
}
算法主要使用向上转型,把五个点定义成父类GeometricObject,通过形状判断后会调用子类重写方法。这样只需要去考虑多边形之间关系,并不要去花费时间去是靠是什么形状,只要它是多边形,都可以集中考虑,这也是使用继承与多态的好处,在GeometricObject也定义一个String属性name,在JudgeShape时对name修改,也可以方便输出的正确性,但是题目还是存在之前的问题没有得以解决,最大复杂度的问题,SourceMonitor的生成报表内容如下

可以看出不仅仅是最大复杂度比上道题还夸张,平均复杂度与平均深度也超标,这道题目将近700行代码也可以看出很多地方的混乱与不清晰,在下面的改进中我会对算法进行优化,先给出这次PTA题目的图像PowerDesigner的相应类图

采坑心得
本次blog分享了期中考试和第三和第四次的PTA,其中期中考试难度较低,需要注意的是在 7-3 点线面问题再重构(容器类)需要注意删除容器中第index - 1的范围,index-1的范围应该是大于等于0并且小于等于elements.size()的,题目主要测试点也都考察了这些问题,我在第一次编写代码的时候并没有考虑等于0的情况导致只得了20分,其他就没有什么要说的了。PTA部分遇到了太多问题,但很多问题并不具有代表性,大多来自于我的粗心或者是考虑不周,我主要分享一些比较关键性的错误,在 7-2 点线形系列4-凸四边形的计算 对于4个点构成三角形合法判定要尤为注意,因为x y x s的情况非常特殊

这种情况也是合法的,这个的判定在 7-1 点线形系列5-凸五边形的计算-1 也有进阶的考量,判断五个点满足四边形和三角形,也存在x y x s的情况,也是需要注意的地方。除此之外在 7-2 点线形系列5-凸五边形的计算-2 计算交叉面积的时候,我直接去计算两个图像交点去重,并没有考虑顺序关系,导致使用鞋带定理计算的面积错误,因为鞋带定理需要是顺序的点排列,相同坐标不同排列使用鞋带定理计算的面积不同,这里可以使用向量叉乘法进行解决。
改进建议
对于期中考试题目,并没有什么可以改进的,一是因为题目简单,代码量本身就不多,其次是因为大致类关系都是给与相应模板的,能修改的地方有限。然而对于PTA几次的作业可以进行修改的地方有很多,可以综合分析PTA的三道题目,其实在很大程度上对于继承的关系有一些滥用,而且方法设计上也存在很多瑕疵,比如计算面积的方面,三角形、四边形、五边形都设计了一个非常麻烦的算法,写完题目想想这些本都可以不需要用到多态,可以直接在父类函数中得到实际算法,public Polygon(Point... pointArr)的构造方法去自动识别传入的Point数量,通过一个综合的isPolygon来判断形状,对传入的点数组用length获取长度,可以直接使用鞋带定理判断面积。没有之前想到使用鞋带定理去判断的每个多边形面积是一开始对这个并不了解,了解后这便是可以优化的一个点。除了计算面积之外包括周长的判断,凹凸多边形、点与多边形关系,线与多边形截取面积等都可以通过对传入的点数组使用length判断长度后进行综合改善,改善后的Polygon类如下
public class Polygon {
private final Point[] pointArr;
private final Line[] lineArr;
public String polyName = "polygon";
public Polygon(Point... pointArr) {
this.pointArr = pointArr;
lineArr = new Line[pointArr.length];
for (int i = 0; i < pointArr.length; i++)
lineArr[i] = new Line(pointArr[i], pointArr[(i + 1) % pointArr.length]);
}
public boolean isPolygon() { //多边形判定
for (int i = 0; i < pointArr.length; i++) {
for (int j = 1; j < pointArr.length; j++) {
if (pointArr[i].pointDistance(pointArr[(i + j) % pointArr.length]) == 0) return false;
if (!lineArr[i].isParallel(lineArr[(i + j) % pointArr.length]) && lineArr[i].isInside(lineArr[i].Intersection(lineArr[(i + j) % pointArr.length]))
&& lineArr[(i + j) % pointArr.length].isInside(lineArr[i].Intersection(lineArr[(i + j) % pointArr.length])))
return false;
}
if (lineArr[i].isParallel(lineArr[(i + 1) % pointArr.length])) return false;
}
switch (pointArr.length) {
case 3:
polyName = "triangle";
break;
case 4:
polyName = "quadrilateral";
break;
case 5:
polyName = "pentagon";
break;
default:
return false;
}
return true;
}
public double getArea() { //计算面积
double sum = 0;
for (int i = 0; i < pointArr.length; i++) {
sum += 0.5 * (pointArr[i].x * pointArr[(i + 1) % pointArr.length].y - pointArr[i].y * pointArr[(i + 1) % pointArr.length].x);
}
return Math.abs(sum);
}
public double getPerimeter() { //计算周长
double sum = 0;
for (int i = 0; i < pointArr.length; i++) {
sum += pointArr[i].pointDistance(pointArr[(i + 1) % pointArr.length]);
}
return sum;
}
public boolean isConvexPolygon() { //判断凹凸多边形
for (int i = 0; i < pointArr.length; i++) {
if (!new Polygon(pointArr[(i + 1) % 5], pointArr[(i + 2) % 5], pointArr[(i + 3) % 5], pointArr[(i + 4) % 5]).isPolygon()
|| new Polygon(pointArr[(i + 1) % 5], pointArr[(i + 2) % 5], pointArr[(i + 3) % 5], pointArr[(i + 4) % 5]).isOut(pointArr[i]))
return false;
}
return true;
}
public boolean isOut(Point n1) { //判断是否在内部
double triangleAreaSum = 0;
for (int i = 0; i < pointArr.length; i++) {
if (!new Line(pointArr[i], pointArr[(i + 1) % pointArr.length]).isParallel(new Line(pointArr[i], n1))) {
triangleAreaSum += new Polygon(n1, pointArr[i], pointArr[(i + 1) % pointArr.length]).getArea();
}
}
return Math.abs(triangleAreaSum - getArea()) < 0.000001;
}
public boolean isOnPolygon(Point n1) { //判断是否在边上
for (int i = 0; i < pointArr.length; i++) {
if (pointArr[i].pointDistance(n1) == 0) return true;
if (lineArr[i].isInside(n1)) return true;
}
return false;
}
public double[] partialArea(Line l) { //获取线和多边形相切的面积
double[] partArea = new double[2];
Point[] crossPoint = new Point[10];
int temp = 0;
for (int i = 0; i < pointArr.length; i++) {
if (l.VerticalDistance(pointArr[i]) > 0) crossPoint[temp++] = pointArr[i];
if (lineArr[i].isInside(l.Intersection(lineArr[i])) || pointArr[i].pointDistance(l.Intersection(lineArr[i])) == 0)
crossPoint[temp++] = l.Intersection(lineArr[i]);
}
partArea[1] = new Polygon(Judgment.ArrayDeformation(java.util.Arrays.copyOf(crossPoint, temp))).getArea();
partArea[0] = Math.min(partArea[1], this.getArea() - partArea[1]);
partArea[1] = Math.max(partArea[0], this.getArea() - partArea[0]);
return partArea;
}
public final int polygonRelationship(Polygon polygon) { //判断两个多边形关系
int temp1 = 0, temp2 = 0;
for (Point point : pointArr) {
if (polygon.isOnPolygon(point)) temp1 += 0;
else if (polygon.isOut(point)) {
temp1 += 1;
} else temp1 += 6;
}
for (Point point : polygon.pointArr) {
if (isOnPolygon(point))
temp2 += 0;
else if (isOut(point)) {
temp2 += 1;
} else temp2 += 6;
}
if (temp1 == pointArr.length * 6 && temp2 == polygon.pointArr.length * 6) return 1;
else if (temp1 == 0 && temp2 == 0 && getArea() == polygon.getArea()) return 3;
else if (temp1 <= 5 && temp2 >= 6 && getArea() < polygon.getArea()) return 4;
else if (temp2 <= 5 && temp1 >= 6 && getArea() > polygon.getArea()) return 6;
else if (isConnected(polygon)) return 2;
else return 5;
}
private boolean isConnected(Polygon polygon) { //判断连接
for (int i = 0; i < lineArr.length; i++) {
for (double j = 0; j < 1; j += 0.25) {
Point point = new Point(pointArr[i].x - (pointArr[i].x - pointArr[(i + 1) % lineArr.length].x) * j, pointArr[i].y - (pointArr[i].y - pointArr[(i + 1) % lineArr.length].y) * j);
if (polygon.isOut(point) && !polygon.isOnPolygon(point)) return false;
}
}
return true;
}
public final Point[] getCrossObject(Polygon polygon) { //返回图像相交点
Point[] crossPoint = new Point[10];
int temp = 0;
for (int i = 0; i < lineArr.length; i++) {
if (polygon.isOut(pointArr[i]) && !polygon.isOnPolygon(pointArr[i])) crossPoint[temp++] = pointArr[i];
for (int j = 0; j < polygon.lineArr.length; j++) {
if (polygon.lineArr[j].isParallel(lineArr[i])) {
if (polygon.lineArr[j].isInside(pointArr[i])) crossPoint[temp++] = pointArr[i];
if (lineArr[i].isInside(polygon.pointArr[j])) crossPoint[temp++] = polygon.pointArr[j];
if (polygon.pointArr[j].pointDistance(pointArr[i]) == 0) crossPoint[temp++] = polygon.pointArr[j];
continue;
}
Point point = polygon.lineArr[j].Intersection(lineArr[i]);
if ((polygon.lineArr[j].isInside(point) || point.pointDistance(polygon.pointArr[j]) == 0)
&& (lineArr[i].isInside(point) || point.pointDistance(pointArr[i]) == 0)) {
crossPoint[temp++] = point;
}
}
}
return java.util.Arrays.copyOf(crossPoint,temp);
}
}
使用更加简洁精炼的算法去计算各种四边形的关系,也可以避免设计一些没有意义的方法导致阅读苦难,甚至在这道题目中不需要去构建Triangle等子图像类,不需要去深究这个图像到底是什么,只需要知道它是多边形即可,对于Point和Line类也可以进行精简,删除一些没有用到的算法
改进后Point类 (点击以展开)
public class Point {
public double x = 0;
public double y = 0;
public Point(double x, double y) {
this.x = x;
this.y = y;
}
public double pointDistance(Point n1) {
return Math.sqrt(Math.pow(this.x - n1.x, 2) + Math.pow(this.y - n1.y, 2));
}
}
改进后Line类 (点击以展开)
public class Line {
private final Point[] pointArr = new Point[2];
private double length = 0;
private double a = 0, b = 0, c = 0;
public Line(Point n1, Point n2) {
pointArr[0] = n1;
pointArr[1] = n2;
this.length = n1.pointDistance(n2);
this.a = n1.y - n2.y;
this.b = n2.x - n1.x;
this.c = n1.x * n2.y - n2.x * n1.y;
}
public void isLine() {
if (pointArr[0].x == pointArr[1].x && pointArr[0].y == pointArr[1].y) {
System.out.println("points coincide");
System.exit(0);
}
}
public double slope() {
if (this.pointArr[0].x == this.pointArr[1].x) return 999;
else return -a / b;
}
public double VerticalDistance(Point n1) {
return (a * n1.x + b * n1.y + c) / Math.sqrt(Math.pow(a, 2) + Math.pow(b, 2));
}
public boolean isParallel(Line l1) {
return Math.abs(this.slope() - l1.slope()) < 0.000001;
}
public Point Intersection(Line l1) {
double det = l1.a * this.b - this.a * l1.b;
if (det == 0) return new Point(999, 999);
double x = (this.c * l1.b - l1.c * this.b) / det;
double y = (this.a * l1.c - l1.a * this.c) / det;
return new Point(x, y);
}
public boolean isInside(Point n1) {
return Math.abs(n1.pointDistance(pointArr[0]) + n1.pointDistance(pointArr[1]) - this.length) < 0.000001 && n1.pointDistance(pointArr[0]) >= 0.000001 && n1.pointDistance(pointArr[1]) >= 0.000001;
}
}
将Judgment类进行优化,并将判断形状JudgeShape方法放入该类,使这个类型彻底成为一个工具类,帮助主函数去剔除亦或者是调用数据或者是其他类的方法,使类更加明确职能,不仅是增强代码的算法精炼程度,更是优化可读性,方便其他人阅读和后期维护修改升级,改进后Judgment类如下
import java.math.RoundingMode;
import java.text.DecimalFormat;
import java.util.concurrent.Callable;
public class Judgment {
public int value = 0;
public Point[] pointArr;
public Judgment(String str) {
String regex = "(\\d:)?([+|-]?(0|[1-9]\\d*)(\\.\\d+)?,[+|-]?(0|[1-9]\\d*)(\\.\\d+)? )+[+|-]?(0|[1-9]\\d*)(\\.\\d+)?,[+|-]?(0|[1-9]\\d*)(\\.\\d+)?( )?";
if (!str.matches(regex)) {
System.out.println("Wrong Format");
System.exit(0);
} else {
this.value = Integer.parseInt(str.split(":")[0]);
str = str.split(":")[1];
pointArr = new Point[str.split(" ").length];
int temp = 0;
for (String point : str.split(" ")) {
this.pointArr[temp++] = new Point(Double.parseDouble(point.split(",")[0]), Double.parseDouble(point.split(",")[1]));
}
}
}
public void isBeyondNum(int n) {
if (pointArr.length != n) {
System.out.println("wrong number of points");
System.exit(0);
}
}
public static String PrintJudge(double x) {
DecimalFormat nf = new DecimalFormat("0.0##");
nf.setRoundingMode(RoundingMode.HALF_UP);
return nf.format(x);
}
private static boolean isVector(Point o, Point a, Point b) {
if (a.x >= 0 && b.x < 0)
return true;
if (a.x == 0 && b.x == 0)
return a.y > b.y;
Point n1 = new Point(a.x - o.x, a.y - o.y);
Point n2 = new Point(b.x - o.x, b.y - o.y);
if (n1.x * n2.y == n2.x * n1.y) {
return a.pointDistance(o) > b.pointDistance(o);
}
return n1.x * n2.y - n2.x * n1.y > 0;
}
public static Point[] ArrayDeformation(Point[] PointArr) {
int temp = 0;
Point[] crossPoint = new Point[PointArr.length];
for (Point point : PointArr) {
int flag = 0;
for (int j = 0; j < temp; j++) {
if (point.pointDistance(crossPoint[j]) == 0) flag = 1;
}
if (flag == 0) crossPoint[temp++] = point;
}
double nx = 0, ny = 0;
for (int i = 0; i < temp; i++) {
nx += crossPoint[i].x;
ny += crossPoint[i].y;
}
Point o = new Point(nx / temp, ny / temp), t = null;
for (int i = 0; i < temp - 1; i++) {
for (int j = 0; j < temp - i - 1; j++) {
if (isVector(o, crossPoint[j], crossPoint[j + 1])) {
t = crossPoint[(j) % temp];
crossPoint[(j) % temp] = crossPoint[(j + 1) % temp];
crossPoint[(j + 1) % temp] = t;
}
}
}
return java.util.Arrays.copyOf(crossPoint, temp);
}
public static class JudgeShape implements Callable<Polygon> {
private final Point[] pointArr;
public JudgeShape(Point... pointArr) {
this.pointArr = pointArr;
}
@Override
public Polygon call() {
for (int i = 0; i < 5; i++) {
Polygon triangle = new Polygon(pointArr[i], pointArr[(i + 3) % 5], pointArr[(i + 4) % 5]);
if (new Line(pointArr[i], pointArr[(i + 3) % 5]).isInside(pointArr[(i + 1) % 5]) && new Line(pointArr[i], pointArr[(i + 3) % 5]).isInside(pointArr[(i + 2) % 5])) {
if (triangle.isPolygon()) {
return triangle;
}
}
triangle = new Polygon(pointArr[(i + 2) % 5], pointArr[(i + 3) % 5], pointArr[(i + 4) % 5]);
if (pointArr[(i + 1) % 5].pointDistance(pointArr[(i + 3) % 5]) == 0 && (pointArr[(i + 2) % 5].pointDistance(pointArr[i]) == 0 || pointArr[(i + 4) % 5].pointDistance(pointArr[i]) == 0 ||
new Line(pointArr[(i + 2) % 5], pointArr[(i + 4) % 5]).isInside(pointArr[i]))) {
if (triangle.isPolygon()) {
return triangle;
}
}
triangle = new Polygon(pointArr[(i + 1) % 5], pointArr[(i + 4) % 5], pointArr[i]);
if (pointArr[i].pointDistance(pointArr[(i + 2) % 5]) == 0 && (pointArr[(i + 2) % 5].pointDistance(pointArr[(i + 3) % 5]) == 0 || pointArr[(i + 4) % 5].pointDistance(pointArr[(i + 3) % 5]) == 0 ||
new Line(pointArr[(i + 2) % 5], pointArr[(i + 4) % 5]).isInside(pointArr[(i + 3) % 5]))) {
if (triangle.isPolygon()) {
return triangle;
}
}
triangle = new Polygon(pointArr[(i + 1) % 5], pointArr[(i + 3) % 5], pointArr[(i + 4) % 5]);
if ((pointArr[(i + 1) % 5].pointDistance(pointArr[i]) == 0 || pointArr[(i + 4) % 5].pointDistance(pointArr[i]) == 0 ||
new Line(pointArr[(i + 1) % 5], pointArr[(i + 4) % 5]).isInside(pointArr[i])) &&
(pointArr[(i + 1) % 5].pointDistance(pointArr[(i + 2) % 5]) == 0 || pointArr[(i + 3) % 5].pointDistance(pointArr[(i + 2) % 5]) == 0 ||
new Line(pointArr[(i + 1) % 5], pointArr[(i + 3) % 5]).isInside(pointArr[(i + 2) % 5]))) {
if (triangle.isPolygon()) {
return triangle;
}
}
Polygon quadrilateral = new Polygon(pointArr[(i + 1) % 5], pointArr[(i + 2) % 5], pointArr[(i + 3) % 5], pointArr[(i + 4) % 5]);
if (quadrilateral.isPolygon()) {
if (pointArr[(i + 2) % 5].pointDistance(pointArr[i]) == 0 || pointArr[(i + 1) % 5].pointDistance(pointArr[i]) == 0 || pointArr[(i + 4) % 5].pointDistance(pointArr[i]) == 0
|| pointArr[(i + 3) % 5].pointDistance(pointArr[i]) == 0 || new Line(pointArr[(i + 1) % 5], pointArr[(i + 4) % 5]).isInside(pointArr[i])) {
return quadrilateral;
}
}
}
Polygon pentagon = new Polygon(pointArr[0], pointArr[1], pointArr[2], pointArr[3], pointArr[4]);
if (pentagon.isPolygon()) {
return pentagon;
} else {
System.out.println("not a polygon");
System.exit(0);
}
return pentagon;
}
}
}
通过上述设计,可以去更加精炼的运行PTA5的两次作业,PowerDesigner的相应类图如下

类图上也可以更加清晰的了解不同类的职能对于HW5_1对应的是 7-1 点线形系列5-凸五边形的计算-1 相关的Main函数和SourceMonitor的生成报表内容如下
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
String arr = scanner.nextLine();
Judgment judgment = new Judgment(arr);
Point[] pointArr = judgment.pointArr;
Polygon polygon;
switch (judgment.value) {
case 1:
judgment.isBeyondNum(5);
polygon = new Polygon(pointArr);
System.out.println(polygon.isPolygon());
break;
case 2:
judgment.isBeyondNum(5);
polygon = new Polygon(pointArr);
if (!polygon.isPolygon()) System.out.println("not a pentagon");
else if (polygon.isConvexPolygon())
System.out.println(polygon.isConvexPolygon() + " " + Judgment.PrintJudge(polygon.getPerimeter()) + " " + Judgment.PrintJudge(polygon.getArea()));
else System.out.println(false);
break;
case 3:
judgment.isBeyondNum(7);
Line line = new Line(pointArr[0], pointArr[1]);
line.isLine();
polygon = new Judgment.JudgeShape(pointArr[2], pointArr[3], pointArr[4], pointArr[5], pointArr[6]).call();
if (polygon.partialArea(line)[0] == 0)
System.out.println("The line is coincide with one of the lines");
else
System.out.println("2 " + Judgment.PrintJudge(polygon.partialArea(line)[0]) + " " + Judgment.PrintJudge(polygon.partialArea(line)[1]));
break;
default:
System.out.println("Wrong Format");
}
}
}

虽然最大复杂度依然很大,但是平均深度和平均复杂度等好了很多,代码更加简洁也更加规范。而在HW5_2对应的便是 7-2 点线形系列5-凸五边形的计算-2 ,因为要重复处理判断两个多边形的形状,所以我想到了应用java并发的思想,虽然有点大材小有,但是这也是一个探究学习的过程,我查阅很多关于JAVA并发的文档,例如JAVA并发 。HW5_2如下
import java.util.Scanner;
import java.util.concurrent.*;
public class HW5_2 {
public static void main(String[] args) throws ExecutionException, InterruptedException {
Scanner scanner = new Scanner(System.in);
String arr = scanner.nextLine();
Judgment judgment = new Judgment(arr);
Point[] pointArr = judgment.pointArr;
Polygon polygon1;
Polygon polygon2;
switch (judgment.value) {
case 4 -> {
judgment.isBeyondNum(10);
Judgment.JudgeShape judge1 = new Judgment.JudgeShape(pointArr[0], pointArr[1], pointArr[2], pointArr[3], pointArr[4]);
Judgment.JudgeShape judge2 = new Judgment.JudgeShape(pointArr[5], pointArr[6], pointArr[7], pointArr[8], pointArr[9]);
ExecutorService service = Executors.newFixedThreadPool(3);
Future<Polygon> r1 = service.submit(judge1);
Future<Polygon> r2 = service.submit(judge2);
polygon1 = r1.get();
polygon2 = r2.get();
service.shutdownNow();
if (polygon1.polygonRelationship(polygon2) == 1)
System.out.println("no overlapping area between the previous " + polygon1.polyName + " and the following " + polygon2.polyName);
else if (polygon1.polygonRelationship(polygon2) == 2)
System.out.println("the previous " + polygon1.polyName + " is connected to the following " + polygon2.polyName);
else if (polygon1.polygonRelationship(polygon2) == 3)
System.out.println("the previous " + polygon1.polyName + " coincides with the following " + polygon2.polyName);
else if (polygon1.polygonRelationship(polygon2) == 4)
System.out.println("the previous " + polygon1.polyName + " is inside the following " + polygon2.polyName);
else if (polygon1.polygonRelationship(polygon2) == 5)
System.out.println("the previous " + polygon1.polyName + " is interlaced with the following " + polygon2.polyName);
else
System.out.println("the previous " + polygon1.polyName + " contains the following " + polygon2.polyName);
}
case 5 -> {
judgment.isBeyondNum(10);
Judgment.JudgeShape judge1 = new Judgment.JudgeShape(pointArr[0], pointArr[1], pointArr[2], pointArr[3], pointArr[4]);
Judgment.JudgeShape judge2 = new Judgment.JudgeShape(pointArr[5], pointArr[6], pointArr[7], pointArr[8], pointArr[9]);
ExecutorService service = Executors.newFixedThreadPool(3);
Future<Polygon> r1 = service.submit(judge1);
Future<Polygon> r2 = service.submit(judge2);
polygon1 = r1.get();
polygon2 = r2.get();
service.shutdownNow();
Point[] crossPoint1 = polygon1.getCrossObject(polygon2);
Point[] crossPoint2 = polygon2.getCrossObject(polygon1);
Point[] crossPoint = new Point[crossPoint1.length + crossPoint2.length];
System.arraycopy(crossPoint1, 0, crossPoint, 0, crossPoint1.length);
System.arraycopy(crossPoint2, 0, crossPoint, crossPoint1.length, crossPoint2.length);
System.out.println(Judgment.PrintJudge(new Polygon(Judgment.ArrayDeformation(crossPoint)).getArea()));
}
case 6 -> {
judgment.isBeyondNum(6);
polygon1 = new Judgment.JudgeShape(pointArr[1], pointArr[2], pointArr[3], pointArr[4], pointArr[5]).call();
if (polygon1.isOnPolygon(pointArr[0])) System.out.println("on the " + polygon1.polyName);
else if (polygon1.isOut(pointArr[0])) System.out.println("in the " + polygon1.polyName);
else System.out.println("outof the " + polygon1.polyName);
}
default -> System.out.println("Wrong Format");
}
}
}
总结
通过这次是期中考试和PTA大作业,使我收益颇盛,不仅更加深入的了解面向对象语言JAVA的魅力,对于封装、继承、多态也从模板化的理解到达真正能去实践这些理论。对于代码学习的我来说,这才是真正有成就感的事情。编写代码完成后,我也会去思考改进的方案,比如运用并发,运用鞋带定理等等,这过程又很大培养了我看文档的自学能力,这是课堂之外的学习,容器类的使用让我本能去了解泛型,去了解Lamdba表达式等。学习是一件很酷的事情,只要有着自己想去学习的东西,秉持着孜孜不倦的求知精神,越是困难的东西,征服过后的成就感才越是令人欣喜。

浙公网安备 33010602011771号