PTA题目集1~3的总结性Blog

一丶前言

本学期开展进行了Java语言程序设计与数据结构的学习,也是刚开始了解面向对象,目前已经学习了四周的课程量,在学习期间,老师布置了PTA三次作业,让我收获很多,我发现Java的语法和c语言中的有许多相似之处,又不是完全相同,两者都有自己的特色。第一次作业题目量很多,但是都不难,属于基础中的基础,知识点也都是一些基础类型,例如;java中的输入输出,循环选择的语法,排序语法等基础部分。第二次作业较第一次作业而言,题目量降低了,但是题目的难度逐步提升,很多知识需要去学习,其中像进制转换的方法,单个字符的取定,合并数组等知识点,都让我收获很多。对于第三次作业而言,题目量只有三道题,但是每一道题对于初学者而言都是很有难度的,首先在第三次作业中,知识点是集中在java中类的学习,有类与类之间的关联关系,类的调用,java封装,以及Pattern,Matcher的方法学习,都让我受益匪浅。

二丶设计与分析

(1)第一次PTA作业

对于第一次PTA作业,题目集有八道题目,都不是很有难度,属于基础题型,我认为最后一题7-8是最具有分析价值的,判断三角形的类型。此题考察选择语句与思维逻辑能力。

题目:

 

在写这个题目时,我也困惑了一段时间,首先 要成立三角形,就要满足三角形三条边的成立关系,但是我们输入三条边的话,可能大小顺序不同,这样在证明三角形是否成立时会使用大量的if语句嵌套,略显麻烦,于是我使用Arrays.sort方法先将输入的三条边进行排序

        Scanner in = new Scanner(System.in);
        double n[] = new double[3];
        int i;
        for (i = 0; i < 3; i++) {
            n[i] = in.nextDouble();
        }
        for (i = 0; i < 3; i++) {
            if (n[i] < 1 || n[i] > 200) {
                System.out.println("Wrong Format");
                return;
            }
        }
        Arrays.sort(n);

 

 然后进行判断每条边与边之间的符合关系,是否符合某种类型的三角形。

if (n[0] + n[1] > n[2]) {
        if (n[0] == n[1] && n[1] == n[2]) {
                System.out.println("Equilateral triangle");
            } else if (n[0] == n[1] && n[0] * n[0] + n[1] * n[1] - n[2] * n[2] < 0.00000000001)
                System.out.println("Isosceles right-angled triangle");
            else if (n[2] == n[1] || n[2] == n[0] || n[0] == n[1])
                System.out.println("Isosceles triangle");
            else if (n[0] * n[0] + n[1] * n[1] - n[2] * n[2] < 0.00000000001)
                System.out.println("Right-angled triangle");
            else {
                System.out.println("General triangle");
            }
        } else
            System.out.println("Not a triangle");

但是这个题容易出问题的地方在于判断等腰直角三角形,浮点型的判断未用估计精度值来做判断,这点需要我们注意。

(2)第二次PTA作业

第二次作业的最后两个题目比较具有代表性,首先第一个题目是“ 求下一天 ”,第二个题目是“求前N天”,看到这两个题目我就意识到这两个题应该是属于同一种写法,所以我们只要全力写出一道题,那么下一道题就会少很多问题。

题目1:

求下一天 (30 分)

输入年月日的值(均为整型数),输出该日期的下一天。 其中:年份的合法取值范围为[1820,2020] ,月份合法取值范围为[1,12] ,日期合法取值范围为[1,31] 。 注意:不允许使用Java中和日期相关的类和方法。

要求:Main类中必须含有如下方法,签名如下:

public static void main(String[] args);//主方法 
public static boolean isLeapYear(int year) ;//判断year是否为闰年,返回boolean类型 
public static boolean checkInputValidity(int year,int month,int day);//判断输入日期是否合法,返回布尔值
public static void nextDate(int year,int month,int day) ; //求输入日期的下一天

输入格式:

在一行内输入年月日的值,均为整型数,可以用一到多个空格或回车分隔。

输出格式:

  1. 当输入数据非法及输入日期不存在时,输出“Wrong Format”;
  2. 当输入日期合法,输出下一天,格式如下:Next date is:年-月-日

在这个题目中,主要是需要满足题目要求,使用指点方法来进行写码,并且将题目要求的方法功能实现

    public static void main(String[] args) {
        Scanner input = new Scanner(System.in);
        int x = input.nextInt();
        int y = input.nextInt();
        int z = input.nextInt();
        if (checkInputValidity(x, y, z) == true) {
            nextDate(x, y, z);
        } else {
            System.out.println("Wrong Format");
        }
    }

主要方法:

public static boolean isLeapYear(int year)// 判断year是否为闰年,返回boolean类型;
public static boolean checkInputValidity(int year, int month, int day)// 判断输入日期是否合法,返回布尔值;
public static void nextDate(int year,int month,int day) ; //求输入日期的下一天

 

 

对于判断闰年,我在上学期学习c语言时,经常练习该类题目,所以深有体会,闰年的判断就是能被4整除,但不能被100整除;能被400整除;只要满足这两个条件的年份就是闰年

if (year % 4 == 0 && year % 100 != 0 || year % 400 == 0)
            return true;
        else
            return false;

非常的简单,接下来时判断日期是否合法,首先题目要求的是年份的合法取值范围为[1820,2020] ,月份合法取值范围为[1,12] ,日期合法取值范围为[1,31] ,但是我们还是应该思考在闰年的2月有29天,而平年二月有28天,这点我们也应该注意到。

if (year < 1820 || year > 2020 || month < 1 || month > 12 || day < 1 || day > 31
                || isLeapYear(year) == false && month == 2 && day > 28
                || isLeapYear(year) == true && month == 2 && day > 29) {
            return false;
        } else {
            return true;
        }

判断完成后,我们就开始进行最重要的方法,求下一天,还是同样的问题,要注意闰年的2月有29天,而平年二月有28天。还有每年的最后一天,每月的最后一天等特殊天数,我们都应该考虑到。

int months[] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
        int monthp[] = { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
        if (isLeapYear(year) == true) {
            if (day == monthp[month - 1]) {
                if (month == 12) {
                    year++;
                    month = 1;
                    day = 1;
                } else {
                    month++;
                    day = 1;
                }
            } else {
                day++;
            }
            System.out.println("Next date is:" + year + "-" + month + "-" + day);
        }
        else {
            if (day == months[month - 1]) {
                if (month == 12) {
                    year++;
                    month = 1;
                    day = 1;
                } else {
                    month++;
                    day = 1;
                }
            } else {
                day++;
            }
            System.out.println("Next date is:" + year + "-" + month + "-" + day);
        }

这样的话,将题目分析的很透彻,如此便能轻松解决题目。

接着我们看下一道题”求前N天“

本题题目是:

求前N天 (30 分)

输入年月日的值(均为整型数),同时输入一个取值范围在[-10,10] 之间的整型数n,输出该日期的前n天(当n > 0时)、该日期的后n天(当n<0时)。
其中年份取值范围为 [1820,2020] ,月份取值范围为[1,12] ,日期取值范围为[1,31] 。
注意:不允许使用Java中任何与日期有关的类或方法。

输入格式:

在一行中输入年月日的值以及n的值,可以用一个或多个空格或回车分隔。

输出格式:

  1. 当输入的年、月、日以及n的值非法时,输出“Wrong Format”;
  2. 当输入数据合法时,输出“n days ago is:年-月-日”
这个题目与求下一天的题目有着异曲同工之处,我们也是按照上一题的思路,将上一题的方法可以直接带过来使用,就是最主要的方法不同,求前N天的方法我们需要思考。
public static boolean isLeapYear(int year)// 判断year是否为闰年,返回boolean类型;
public static boolean checkInputValidity(int year, int month, int day)// 判断输入日期是否合法,返回布尔值;
public static void nextDate(int year,int month,int day) ; 求前N天

这里我们要注意到N是可为正,可为负的,如果n是小于0的时候,求的是后N天,如果n是大于0的时候,求的是前N天,其次就是上一题我们注意的问题,本题也需要注意要注意闰年的2月有29天,而平年二月有28天,如果输入的N加上日期超过了本月,月份就要增加,类似,如果月份超过12月,年份就要增加,月份就要更新为1月份,这些我们也都需要考虑到。所以我写的求前n天的方法为:

public static void nextDate(int year, int month, int day) // 求输入日期的下一天
    {
        int months[] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
        int monthp[] = { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
        if (isLeapYear(year) == true) {
            if (day == monthp[month - 1]) {
                if (month == 12) {
                    year++;
                    month = 1;
                    day = 1;
                } else {
                    month++;
                    day = 1;
                }
            } else {
                day++;
            }
            System.out.println("Next date is:" + year + "-" + month + "-" + day);
        }
        else {
            if (day == months[month - 1]) {
                if (month == 12) {
                    year++;
                    month = 1;
                    day = 1;
                } else {
                    month++;
                    day = 1;
                }
            } else {
                day++;
            }
            System.out.println("Next date is:" + year + "-" + month + "-" + day);
        }
    }

当然跨年的测试,闰年3月初,平年3月初,8月初,9月初,求下n天平年2月,求下n天闰年2月,求下n天闰年12月等我们都要带入数据去测试,其他和上一题求下一天类似。

(3)第三次PTA作业

对于第三次作业,我认识是三次题目集中,难度系数最大的,虽然它只有三道题目,但是每一道都很有难度,第一题为创建一个账户类,第二题为创建一个日期类,第三题为一元多项式求导。在这里,我分析后两道题目

首先是第二题, 定义日期类

题目为:

定义日期类 (28 分)
 

定义一个类Date,包含三个私有属性年(year)、月(month)、日(day),均为整型数,其中:年份的合法取值范围为[1900,2000] ,月份合法取值范围为[1,12] ,日期合法取值范围为[1,31] 。 注意:不允许使用Java中和日期相关的类和方法,否则按0分处理。

要求:Date类结构如下图所示:

类图.jpg

输入格式:

在一行内输入年月日的值,均为整型数,可以用一到多个空格或回车分隔。

输出格式:

  • 当输入数据非法及输入日期不存在时,输出“Date Format is Wrong”;
  • 当输入日期合法,输出下一天,格式如下:Next day is:年-月-日
该题目设计类的创建,所以在写这个题目之前,我又再次学习了类的知识,我们可以按照题目要求的类图来写。
首先先是主方法:
  public static void main(String[] args) {
        Scanner input = new Scanner(System.in);
        int x = input.nextInt();
        int y = input.nextInt();
        int z = input.nextInt();
        Date haha=new Date();
        if (haha.checklnputValidity(x, y, z) == true) {
            haha.getNextday(x,y,z);
        }
        else {
            System.out.println("Date Format is Wrong");
        }
    }

接着按照类图的属性和方法来定义Date类:

class Date {
    private int year;
    private int month;
    private int day;
    public Date(){}
    void yeargetter(int year){
          this.year=year;
    }
    int yearsetter(){
        return year;
    }
    void monthgetter(int month){
        this.month=month;
    }
    int monthsetter(){
        return month;
    }
    void daygetter(int day){
        this.day=day;
    }
    int daysetter(){
        return day;
    }
    boolean isLeapYear(int year){
        if (year % 4 == 0 && year % 100 != 0 || year % 400 == 0)
            return true;
        else
            return false;

    }
    boolean checklnputValidity(int year,int month,int day){
        if (year < 1900 || year > 2000 || month < 1 || month > 12 || day < 1 || day > 31
                || isLeapYear(year) == false && month == 2 && day > 28
                || isLeapYear(year) == true && month == 2 && day > 29) {
            return false;
        } else {
            return true;
        }
    }
    void getNextday(int year, int month, int day){
        int months[] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
        int monthp[] = { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
        if (isLeapYear(year) == true) {
            if (day == monthp[month - 1]) {
                if (month == 12) {
                    year++;
                    month = 1;
                    day = 1;
                } else {
                    month++;
                    day = 1;
                }
            } else {
                day++;
            }
            System.out.println("Next day is:" + year + "-" + month + "-" + day);
        }
        else {
            if (day == months[month - 1]) {
                if (month == 12) {
                    year++;
                    month = 1;
                    day = 1;
                } else {
                    month++;
                    day = 1;
                }
            } else {
                day++;
            }
            System.out.println("Next day is:" + year + "-" + month + "-" + day);
        }

    }

}

 

在写Date类使,我们也要特别注意类的封装,明白且正确使用类的封装。
 
接着,我们来看本次作业最最最最最难得一道题目(个人感觉),因为这个题目我没有完全写正确,只得了五分之三分数:

 

 

题目是多项式求导:
一元多项式求导(类设计) (50 分)
 

编写程序性,实现对简单多项式的导函数进行求解。详见作业指导书。

输入格式:

在一行内输入一个待计算导函数的表达式,以回车符结束。

输出格式:

  1. 如果输入表达式不符合上述表达式基本规则,则输出“Wrong Format”。
  2. 如果输入合法,则在一行内正常输出该表达式的导函数,注意以下几点: 结果不需要排序,也不需要化简;
  • 当某一项为“0”时,则该项不需要显示,但如果整个导函数结果为“0”时,则显示为“0”;
  • 当输出结果第一项系数符号为“+”时,不输出“+”;
  • 当指数符号为“+”时,不输出“+”;
  • 当指数值为“0”时,则不需要输出“x^0”,只需要输出其系数即可。

emmm,拿到这个题目时,我刚开始无从下手,人都傻了,最后在老师的指导下,学习了正则表达式,如此便有了一定的思路头绪。

首先,我先将输入的多项式进行判断,如果有空格存在,就删掉空格,然后在进行判断是否符合多项式的规则,代码如下:

Scanner in = new Scanner(System.in);
        String biaodashi=in.nextLine();
        String biaodashi1=biaodashi.replaceAll(" ","");
        panduan Panduan=new panduan();
        if(Panduan.lll(biaodashi1)){
            Panduan.ppp(biaodashi1);
        }
        else {
            System.out.println("Wrong Format");
        }

如果输入的字符串符合多项式的要求,那么就进行多项式的拆解,在这里运用正则表达式,我的正则表达式有很多,来进行多项式的多种情况,例如:

 String aa="[-+]?";

    String bb="([0-9]*)?\\*?";
    String cc="(x(\\^[+-]?[0-9]+)?)?";
    String dd="(\\s*)?";
    String totalRegex = "([-+]?([1-9]+[0-9]*(\\*)?)?x?(\\^[+-]?[0-9]+)?)+";
    String sbuRegex = "[-+]?([1-9]+[0-9]*(\\*)?)?x?(\\^[+-]?[0-9]+)?";
    String  sbuRegex1="[-+]?([1-9]+[0-9]*)";
    String  sbuRegex2="[-+]?([1-9]+[0-9]*)(\\*)x";
    String sbuRegex3 = "[-+]?([1-9]+[0-9]*(\\*))?x(\\^[+-]?[0-9]+)?";

这些正则表达式对应多种情况下的多项式。

接着我写了多个方法来进行判断,判断输入的多项式,满足以上哪一种正则表达式,如果满足,就按照相对应的拆分方法进行拆分,然后在输出求导后的多项式,例如:

void ppp(String biaodashi1) {
        boolean bl = Pattern.matches(sbuRegex1, biaodashi1);
        if (bl == true) {
            System.out.println(0);
        }

        boolean b2 = Pattern.matches(sbuRegex2, biaodashi1);
        if (b2 == true) {
            String[] arr1 = biaodashi1.split("\\*x");
            BigInteger m = BigInteger.valueOf(Integer.parseInt(arr1[0]));
            for (int i = 0; i < arr1.length; i++) {
                System.out.println(m);
            }
        }
        else {
            Pattern pattern = Pattern.compile(sbuRegex3);
            Matcher matcher = pattern.matcher(biaodashi1);
            ArrayList<String> List = new ArrayList<String>();
            ArrayList<String> shuList = new ArrayList<>();
            while (matcher.find()) {

                String tmp = matcher.group();
                List.add(tmp);
                int begin = matcher.start();
                int end = matcher.end();
               // System.out.println("项:"+tmp+" begin:"+begin+"    end:"+ (end-1));
                if (end == biaodashi1.length())
                    break;
            }

            for (int i = 0; i < List.size(); i++) {
                boolean b3 = Pattern.matches("[-+]?([1-9]+[0-9]*(\\*))x(\\^[+-]?[0-9]+)", List.get(i));
                boolean b4 = Pattern.matches("[-+]?x(\\^[+-]?[0-9]+)", List.get(i));
                boolean b5 = Pattern.matches("[-+]?x", List.get(i));
                if (b3 == true) {
                    String[] arr2 = List.get(i).split("\\*x\\^");
                    BigInteger m = BigInteger.valueOf(Integer.parseInt(arr2[0]));
                    BigInteger n = BigInteger.valueOf(Integer.parseInt(arr2[1]));
                    BigInteger k = new BigInteger("0");
                    BigInteger q1 = new BigInteger("1");
                    if(n.compareTo(k)==0){
                        System.out.print("Wrong Format");
                        break;
                    }
                    else {
                        if (i == 0)
                            System.out.print(m.multiply(n) + "*x^" + n.subtract(q1));
                        else {
                            if (m.multiply(n).compareTo(k)==1)
                                System.out.print("+" + m.multiply(n)+ "*x^" + n.subtract(q1));
                            if (m.multiply(n).compareTo(k)==-1)
                                System.out.print(m.multiply(n)+ "*x^" +n.subtract(q1));
                        }
                    }
                }
                else if(b4==true&&i==0){

                    String[] arr3 = List.get(i).split("x\\^");
                    String b="-";
                    if(arr3[0].equals(b)){
                        BigInteger p = BigInteger.valueOf(Integer.parseInt(arr3[1]));
                        BigInteger k = new BigInteger("2");
                        BigInteger q1 = new BigInteger("1");
                        if(p.compareTo(k)==0){
                            System.out.print("-"+p+"*x");
                        }
                        else
                        System.out.print("-"+p + "*x^" + p.subtract(q1));
                    }
                    else{
                        BigInteger p = BigInteger.valueOf(Integer.parseInt(arr3[1]));
                        BigInteger k = new BigInteger("2");
                        BigInteger q1 = new BigInteger("1");
                        if(p.compareTo(k)==0){
                            System.out.print("-"+p + "*x");
                        }
                       else
                           System.out.print("-"+p + "*x" +p.subtract(q1) );
                    }
                   /* for(int q = 0;q< arr3.length;q++){
                        System.out.println(arr3[q]);
                    }*/
                }
                else if(b5==true){
                    String[] arr4 = List.get(i).split("x");
                    String b="-";
                    if(arr4[0].equals(b)){
                        System.out.print(-1);
                    }
                    else
                        System.out.print(1);
                }
                else   {

                        String[] arr5 = List.get(i).split("\\*x");
                     BigInteger m = BigInteger.valueOf(Integer.parseInt(arr5[0]));
                        System.out.print(m);

                }
            }

这个题目我已经在尽力在写,但是还有几个点无法通过,可能还是代码有漏洞。

 

三丶踩坑心得

(1)第一次作业PTA的作业中,不仅要判断输入是否合法,还要准确的判断出三角型类型,其中判断顺序的先后是有讲究的,比如等腰和等边,如果先对等腰进行判断,等边的结果就无法出现。其次就是精度问题,也困扰了我很久,因为例如直角三角形中根号二的情况。

Arrays.sort(n);
if (n[0] + n[1] > n[2]) {
if (n[0] == n[1] && n[1] == n[2]) {
System.out.println("Equilateral triangle");
} else if (n[0] == n[1] && n[0] * n[0] + n[1] * n[1] - n[2] * n[2] < 0.00000000001)
System.out.println("Isosceles right-angled triangle");
else if (n[2] == n[1] || n[2] == n[0] || n[0] == n[1])
System.out.println("Isosceles triangle");
else if (n[0] * n[0] + n[1] * n[1] - n[2] * n[2] < 0.00000000001)
System.out.println("Right-angled triangle");
else {
System.out.println("General triangle");
}
} else
System.out.println("Not a triangle");
}
}

(2)第二次作业中,这两个题目求下一天和求前N天的注意事项都差不多,我认为最要注意的就是边界点的注意,像跨年的测试,闰年3月初,平年3月初,8月初,9月初,求下n天平年2月,求下n天闰年2月,求下n天闰年12月等

 

 

 还有就是像答案的测试点的数据,我都带进去将每一个点都通过才行。

 

 对应的输入输出。

 

 (3)第三次PTA作业

第三次作业需要注意的坑有很多,首先我们要理解类与对象的关系,对象是类的一个实例,有状态和行为。例如,一条狗是一个对象,它的状态有:颜色、名字、品种;行为有:摇尾巴、叫、吃等。类是一个模板,它描述一类对象的行为和状态。其次我们要学会类的封装,这也时老师告诉我们最为重要的一点,学习java一定要学会它的封装,类的封装的优点: 良好的封装能够减少耦合。类内部的结构可以自由修改,可以对成员变量进行更精确的控制,隐藏信息,实现细节。

我写的部分封装:

class Date {
private int year;
private int month;
private int day;
public Date(){}
void yeargetter(int year){
this.year=year;
}
int yearsetter(){
return year;
}
void monthgetter(int month){
this.month=month;
}
int monthsetter(){
return month;
}
void daygetter(int day){
this.day=day;
}
int daysetter(){
return day;
}

然后是多项式求导的坑,与其说坑,不如说是不懂得地方,这个题目我觉得难度比较高,我也没有写完全,所以有的坑我也没有发现,但是这个题,一定要去看看Pattern跟Matcher方法的使用,这对这个题目是必须的:

 Pattern pattern = Pattern.compile(sbuRegex3);
            Matcher matcher = pattern.matcher(biaodashi1);
            ArrayList<String> List = new ArrayList<String>();
            ArrayList<String> shuList = new ArrayList<>();

坑有java中大数的运算,大数的运算跟我们平时int型的运算是不一样的,这需要我们去学习,

public class Main {
    public static void main(String[] args) {
        Scanner scan = new Scanner(System.in); 
        BigInteger num1;
        num1 = scan.nextBigInteger();
        BigInteger num2;
        num2 = scan.nextBigInteger();
        //加法
        System.out.println(num1.add(num2));
        //减法
        System.out.println(num1.subtract(num2));
        //乘法
        System.out.println(num1.multiply(num2));
        //除法(相除取整)
        System.out.println(num1.divide(num2));
        //取余
        System.out.println(num1.mod(num2));
        //最大公约数GCD
        System.out.println(num1.gcd(num2));
        //取绝对值
        System.out.println(num1.abs());
        //取反
        System.out.println(num1.negate());
        //取最大值
        System.out.println(num1.max(num2));
        //取最小值
        System.out.println(num1.min(num2));
        //是否相等
        System.out.println(num1.equals(num2));
    }
}

然后就是Arrarylist的使用和学习,这一知识我也是花了功夫去学习,相比于c语言里的链表,Arrarylist的使用更加方便。

 

四丶改进建议

我认为第一次作业中,可以将三角形的判断直接写入一个新的三角形类当中去。可能会比较具有可利用性。

第二次作业中,求下一天,的nextDate方法中,我认为可以进行修改,删去后来的平年情况,直接在闰年中添加if判断。

第三次作业中,多项式求导题目中,我认为该题需要我们将正则表达式通透理解,并且能够熟练运用,我认为我写的需要改进的地方在能把一些重复的方法功能写入同一个方法中。

 

五丶总结

学习了解了类的构造方法、方法的调用、参数传递、对象的构造与使用;充分练习了循环结构、控制结构;掌握了数据的输入与输出;同时学习编写结构清晰、逻辑正确、功能完善的java代码。对课堂模式的看法与建议:课堂采取的是线上线下相结合的模式。线上需通过mooc自学相关Java内容,老师根据题目的完成情况讲产生的学各种知识, 这种课堂模式非常具有创新性,大大增强了课堂效率。当然我也了解到代码的编写过程一定要严谨,数据的处理也是十分重要的 ,一定要去测试代码,测试代码输入的合理性,这也是题目涉及到的非法输入的测试点。 很多东西需要自学,这样也养成了自学的习惯和学会翻阅资料等。做题只靠老师讲的知识 远远不够,需要我们自己去查找资料。

 

posted @ 2021-04-04 18:14  gddd  阅读(201)  评论(0)    收藏  举报