一.前言
过去的一个月进行了Java的初步学习,学习了初步语法,数组循环方法的定义使用以及类和继承多态等进阶知识,通过这一个月的学习大致掌握了java关于C语言的一些不同之处。通过PTA的这三次作业,我大致总结了出题目的规律:通过前面的题的部分逻辑引导我们来解决更复杂的问题,便于我们进行方法的选择,防止我们用一些比较繁琐的方法。其中考察的知识点也是逐层递进,通过布置不同难度的问题来让我们选择更加高级的知识来解决问题。PTA1的题量较大,有9道题,其中前5道题主要是考察我们对于java简单基础语法的运用,而不是考察我们的逻辑等能力,例如第一题和第二题考察了关于数字的计算,以及不同输入下的不同输出情况,使用了if-else语句和for循环解决问题。而从第五题开始,就需要使用字符串数组来存储游戏角色名字等信息,并根据输入的简单数据输出较复杂的数据。从第六题开始就在考察我们对于数据的输入合法性检验问题,提前为之后PTA23中的错误输入做铺垫,第9题中的判断三角形类型也是后面题目中有所考察到的,要先算出边长,在根据边长的平方关系判断三角形类型。而PTA2中更多的是考察关于String类型的输入判断以及提取分析字符串变量当中的数据,例如考察两个字符串的拼接,或者是判断输入的字符串中是否有指定内容,根据输入的不同内容输出不同的情况,在复杂一点就是通过判断字符串数组中的某一个位置是否有非法输入,利用遍历的方法就会非常的复杂,于是就会引用到树上的多种方法来解决,例如字符串转换成char型数组的方法toCharArray()等。PTA2中的第二题较为复杂,主要难点是错误输入的情况太多,不容易将一个大字符串分解成多个小字符串。而PTA3中的三道题是采用了循序递进的原则,第一道题只是简单的计算公式,主要难点是判断输入的合法性数据,由于符号非常多,就会导致错误情况非常多,比较难以判断。而到了第二道题,就比较贴近以用户的角度来编程,用户输入简单的数据,来得到不同的结果,就会导致在不同的算法下判断错误输入的情况会发生改变,就会产生许多的情况,需要依次来判断。而在逻辑计算方面,先是引入了点的概念,其次考察了直线和直线的位置关系,在第三道题中,考察了直线和点关于三角形的一些关系。
二.设计与分析以及踩坑心得
1.PTA2 7-2题目
该题目要求我们根据用户输入的一长串01字符串,根据给定的格式将长字符串分离成小字符串,并且判断输入的数据是否有格式错误和逻辑错误,分别输出三种不同的情况。我采用了分部判断的方法,由于字符串的正确格式为起始位0+8位二进制正常数据+奇偶校验位和结束位1,所以所有的正确数据必然是0开头1结尾,所以我先判断是否满足输入的大字符串是否满足11位数,处于方便我采用了toCharArray()方法,就是将字符串转换成一个char数组,至于为什么不转换成int数组,是为了防止如果用户错误输入了一些符号,符号的ASCLL码可能是0或者1,会导致无法排除这种错误情况。转换成数组后,首先排除不足11位的情况,例如输入0111输出null data,同时利用一个数组遍历,判断输入的数据是
否全都是0或者1,防止输入别的数据例如标点符号。在这里我漏掉了一种情况,那就是空格的情况,我没有将以空格开头和空格结尾的情况考虑进去,这导致我始终有3个测试点无法通过,最后反复查验,增加了if条件的限制如果数据不是0和1的全部为Wrong Format。之后在添加最后一种输入错误的情况,假如什么都不输入则是null data,在这里我有一点犹豫,假如输入了数字,但是没有构成一个具体的完整数据,那么到底应该输出null data还是Wrong Format,之后经过我在pta上对测试点逐个测试,发现只要是输入的错误全都是Wrong Format,只有字符串为空才是null data。解决完输入合法性检验的问题后,要将输入正确的char数组分别存储,我想到了使用char二维数组的方法,首先先将完整的char数组利用arraycopy()方法将他们存进一个char的二维数组,然后再判断输入的数据是否有逻辑错误。补充一点,我对大字符串分类的标准是取0这个数字之后的10位数,将这11位数作为一个数据存入数组中。逻辑错误主要分这么几种:1.没有0这个数字 2.没有以1结尾 3.数据的奇偶校验位错误 4.已经输入了一个完整数据但是之后的数据不够11位。首先每个数组中存的都是11位数,所以对于12这两种情况我只需要判断a【0】和a【10】是不是0和1就可以。而对于4这种情况,我需要在分离字符串的时候判断,如果已经有一个完整的数据,但是第二个起始位0之后没有10位,则不去分离这个数据,相当于他不存在。如果本身就不足11位则在先前已经有过判断。最复杂的是奇偶效验位,所谓奇偶效验,就是根据8位数据的奇数偶数个数在进行结尾的补足,保证整个数据的奇数是奇数个,偶数是偶数个,所以我需要利用数组遍历来统计1的个数,假如是奇数个1,那么就应该补0,反之的话应该补1,所以需要利用bool类型的变量进行判断,以上就是这道题目的全部解析。
2.PTA3 7-1题目
这道题目是之后两道题目的铺垫,要求我们求两个点之间的距离,用户输入的是两个点的坐标,难点并不在于求两点坐标的计算公式,而是在于如何根据输入的数据将两点坐标提取出来,以及输入的合法性判断,相比于上一道题,这道题的合法性检验更加复杂。
首先仍然是解决输入合法性的问题,由于之后两道题输入的数据都和这道题类似,所以关于输入数据合法性的问题在此一遍讲完,由于数据的复杂程度,我将数据类型在此列出来,首先是符号,有空格逗号小数点这三个 他们可能出现在整个数据的前中后三个位置,所以我仍然是首先将整个字符串转换为数组,采用上述方法,然后在首先检测第一个字符是不是数字开头,是不是以数字结尾,不是的话输出Wrong Format,然后采取遍历的方法检测整个字符串中空格和逗号以及小数点的数量是不是一个,确认输入的内容无错误后,才能检测他们的顺序是否有错误,因为所有的点都是以逗号来分割x和y,所以我选择了一个spilt方法,可以将字符串通过某个具体的符号来分割成小字符串,其实根本的的原理和上一道题是差不多的,就是将复杂的东西转换为固定格式的东西,再根据正确的格式挨个检测正误,就像将自然语言转换成逻辑语言一样。先按照空格分开两个点的坐标,在根据逗号分隔x和y,将这几个数据以xyxyxyxy的顺序存入一个double数组。在这里我之前漏掉了一种情况,那就是没有排除“1.”和“.1”还有“01”这三种错误输入,后来我检测,在小数前点之后是不是接着数字来排除这三种。之后就是看数组的长度除以二来判断总共输入了几个点的坐标,第二道题的判断两个点是否重合就是看有没有两个点的xy数据一样,
而第三道题的判断是否能构成三角形就是首先将三个边的长度算出来,我采取了第一道题的两点距离,将三个点两两组合,然后根据三边关系得到是否能够成三角形。所以在每道题目的计算过程中,得到的数据一定是完全正确的。
因为已经获得了完全正确的数据,现在来说算法,在这两道题中,大多数都要和直线有关系,出于直线斜率在竖线的时候不存在,所以我选择采用一般式的直线方程,虽然计算ABC的过程非常繁琐,但是规避了讨论斜率不存在的情况,方便许多。判断是否垂直于x轴,我选择采用向量的方式(点乘等于零)来判断,而点到直线的距离我采用了海伦公式的推论。判断三点是否在一条直线上我也采用了向量,取一个公共点A判断向量AB是否平行于向量AC,四点共线也是这么得出的。第三题中三角形的面积用海伦公式可得,重心坐标也有简单的推导公式,最为困难的在第三题的第四点,我没有一个合适的方法来判断直线和三角形交点的个数,如果使用简单的两直线联立,那么整个步骤将非常繁琐,因为交点有三种情况,012,而直线和三角形有没有交点也就是直线扫过三角形时的三种不同情况,所以我想起了高中学过的线性规划求最值,也就是先将三个关系式列出来,然后通过直线方程来解交点坐标,因为要求面积,所以只需要用大三角形面积减小三角形面积就行了。
三.改进建议以及总结
对于我最后两道题的写法,我觉得过于繁琐,我有很多算法类似的步骤,我完全可以将他们写成一个个方法来省去我的步骤,我设置了很多的变量来,例如条件满足这个变量就为1,不满足就为0这种类似的做法有很多,导致代码的可读性大大降低。
我认为在这两个题目中我最大的收获并不是写出了多么繁琐的算法,而是我在利用double变量进行复杂的次方或者sqrt运算时,发现了double精度不足导致得到的结果不一样,在一开始,我想的办法是用c语言中的printf输出,强行保留位数,或者强行转换成float类型,但是对于一些数据简单的测试,可能并没有到小数点后那么多位,导致结果错误。我在csdn上了解了计算机的计算原理之后,我发现了一个保留尾数还不自动补零的方法,叫做DecimalFormat,原理就是先将数字转换成字符串,然后进行数据整合,最后以字符串的形式输出,这个方法中的#就是不够0自动补齐。
总而言之,这三次题目让我大概熟悉了java中在继承之前所有的知识点,同时也是我学习编程以来做过难度最大的题目之一,为了缩减代码我废了很多心机,甚至翻出了高中的数学笔记本来看有没有更简单的算法,在以后的学习中,我还需要对方法的构建进行更深入的学习,因为有一个好方法,代码的可读性将会大大的提升。同时,我还需要回归课本,书中有许多代码示例,我需要仔细地将他们研读,变为自己的东西,以上就是我对本次题目的总结。