PTA1~3次作业总结_lxh

PTA1~3次作业总结分析
一、前言:
  第一次PTA作业给人的感觉还行,没有任何思维上的难度,就像是在做小学初中数学题,例如身体质量指数(BMI)的测算,长度质量计量单位换算,奇数求和,房产税费计算2022,二进制数值提取等。这些题目基本上都是用编程语言把数学公式表达出来,然后进行输出就可过题,还没有显示出Java语言的特性,以及没有面向对象编程的开始。相比现在学的Java,这些题目用C语言可以更快的写完。第一次题目的知识点主要是Java语言的输入输出,变量的创建与使用,控制语句的基本使用,基本数据类型的转换,循环语句的使用。题量上面稍微有点多,一共有9题,但是难度不大,所以耗时并不多。
        第二次PTA的作业难度至今使我难以忘怀——比第一次的要难100倍,可谓是指数爆炸式增长,因此我觉得的这种出题方式非常不合理,没有做到与上一次作业的衔接,也没有循序渐进的帮助学生消化所学习的知识,第一道题目就是综合性非常强的菜单计价程序1,紧接着第二题是菜单计价程序2,是第一题的升级版(继续增加功能,逻辑复杂度升高),第三题和第四题的难度又陡然下降了许多,第三题是日期类的基本使用,第四题是一道经典的算法题(技巧题,无脑会超时)。虽然题量上面只有四道,但是这些题的难度实在太大,花的时间远多于第一次作业,而且由于能力问题,第二题的完整代码没有写出来,自信心受到挫败。第二次作业的知识点主要是掌握类的定义,类的使用,类与类之间的关系以及相互调用;还有字符串转换成数字,日期中闰年的判断,通过日期判断星期几,字符串的拼接与分割(split0方法的使用)。
  第三次PTA的作业难度和第二次的差不多,也就是除了第一次作业没有太大难度,后面的作业都不简单(大抵是我太菜了,哎)。第一题是最难的,菜单计价程序3,可谓又一次大升级,整个年级最后都没有把这道题完全AK,后面的题考查的知识点就特别单一了,而且与Java语言的特性没有太大关系。例如判断一个数组中是否有重复的数据,以及删除重复的数据,还有排序。其中关于Java的封装性有一道详细的题目,难度不大,主要是让学生熟悉Java语言的封装性这一大特点。当然,每次作业都少不了日期类的题目,这次是要掌握两个日期的间隔天数以及周数(其实都有大佬写好的代码,我们主要是学习,从中感受编程给人们实际带来的方便)。
二、设计与分析:
  1、第一次作业:

  7-1代码:

 代码逻辑很简单,选择语句轻松完成,旨在用代码实现数学计算。

 最后一题稍微有些难度,需要考虑的方面很多。题目如下:

 代码如下:

 

 设计的方向主要是根据数学上三角形形状的判断以及代码上数据的处理规则。

   2、第二次作业:

 这次作业出现了菜单计价程序,也就是最难的题目,而且是一系列的,一次比一次难度大。

题目如下:

7-1 菜单计价程序-1
分数 30
作者 蔡轲
单位 南昌航空大学

某饭店提供4种菜,每种菜品的基础价格如下:
西红柿炒蛋 15
清炒土豆丝 12
麻婆豆腐 12
油淋生菜 9

设计点菜计价程序,根据输入的订单,计算并输出总价格。
订单由一条或多条点菜记录组成,每条记录一行,最后以"end"结束
每条点菜记录包含:菜名、份额两个信息。
份额可选项包括:1、2、3,分别代表小、中、大份)

不同份额菜价的计算方法:
小份菜的价格=菜品的基础价格。
中份菜的价格=菜品的基础价格1.5。
小份菜的价格=菜品的基础价格
2。
如果计算出现小数,按四舍五入的规则进行处理。

参考以下类的模板进行设计:
菜品类:对应菜谱上一道菜的信息。
Dish {
String name;//菜品名称
int unit_price; //单价
int getPrice(int portion)//计算菜品价格的方法,输入参数是点菜的份额(输入数据只能是1/2/3,代表小/中/大份)
}

菜谱类:对应菜谱,包含饭店提供的所有菜的信息。
Menu {
Dish[] dishs ;//菜品数组,保存所有菜品信息
Dish searthDish(String dishName)//根据菜名在菜谱中查找菜品信息,返回Dish对象。
}

点菜记录类:保存订单上的一道菜品记录
Record {
Dish d;//菜品
int portion;//份额(1/2/3代表小/中/大份)
int getPrice()//计价,计算本条记录的价格
}

订单类:保存用户点的所有菜的信息。
Order {
Record[] records;//保存订单上每一道的记录
int getTotalPrice()//计算订单的总价
Record addARecord(String dishName,int portion)
//添加一条菜品信息到订单中。
}

输入格式:

每条点菜记录的格式:
菜名+空格(英文)+份额
注:份额可输入(1/2/3), 1代表小份,2代表中份,3代表大份。
最后一条记录以“end”结束。

输出格式:

订单上所有菜品的总价(整数数值),每份菜
如果订单中包含不能识别的菜名,则在总价之前输出“** does not exist”,**是不能识别的菜名

代码如下:

import java.util.*;
public class Main
{
public static void main(String[] args)
{
Scanner input_xiaohui = new Scanner(System.in);
Dish[] dishes = {
new Dish("西红柿炒蛋", 15),
new Dish("清炒土豆丝", 12),
new Dish("麻婆豆腐", 12),
new Dish("油淋生菜", 9),
};
Menu menu = new Menu(dishes);
Order order = new Order();
while(true)
{
String line_xiaohui = input_xiaohui.nextLine();
if(line_xiaohui.equals("end"))
break;
String[]parts_xiaohui = line_xiaohui.split(" ");
String dishName = parts_xiaohui[0];
int portion = Integer.parseInt(parts_xiaohui[1]);
order.addARecord(dishName,portion,menu);
}
System.out.println(order.getTotalPrice());
}
}
class Dish
{
String name_xiaohui;
int unit_price_xiaohui;
Dish(String name,int price)
{
this.name_xiaohui = name;
this.unit_price_xiaohui = price;
}
int getPrice_xiaohui(int portion)
{
float bl_xiaohui[]={1,1.5f,2};
return (int) Math.round(unit_price_xiaohui*bl_xiaohui[portion-1]);
}
}
class Menu
{
Dish[] dishes;
Menu (Dish[] dishes)
{
this.dishes=dishes;
}
Dish searchDish_hui(String dishName)
{
for(Dish dish:dishes)
{
if(dish.name_xiaohui.equals(dishName))
return dish;
}
return null;
}
}
class Record
{
Dish dish;
int portion;
Record(Dish dish,int portion)
{
this.dish = dish;
this.portion = portion;
}
int getPrice_xiaohui()
{
return dish.getPrice_xiaohui(portion);
}
}
class Order
{
List <Record> records;
Order()
{
this.records = new ArrayList <>();

}
int getTotalPrice()
{
int totalPrice = 0;
for(Record e:records)
{
totalPrice+=e.getPrice_xiaohui();
}
return totalPrice;
}
Record addARecord(String dishName,int portion,Menu menu)
{
Dish dish = menu.searchDish_hui(dishName);
if(dish==null)
{
System.out.println(dishName+" does not exist");
return null;
}
Record record_xiaohui=new Record(dish,portion);
records.add(record_xiaohui);
return record_xiaohui;
}
}

idea类图分析如下:

 

  • SourceMonitor生成报表:

Metrics Details For File 'Main.java'
--------------------------------------------------------------------------------------------

Parameter Value
========= =====
Project Directory C:\Users\Doctor E\IdeaProjects\Test\src\菜单1\
Project Name PTA作业
Checkpoint Name Baseline
File Name Main.java
Lines 81
Statements 49
Percent Branch Statements 10.2
Method Call Statements 7
Percent Lines with Comments 19.8
Classes and Interfaces 5
Methods per Class 1.20
Average Statements per Method 6.33
Line Number of Most Complex Method 39
Name of Most Complex Method Order.addARecord()
Maximum Complexity 3
Line Number of Deepest Block 43
Maximum Block Depth 3
Average Block Depth 1.47
Average Complexity 1.80

--------------------------------------------------------------------------------------------
Most Complex Methods in 3 Class(es): Complexity, Statements, Max Depth, Calls

Dish.Dish() 1, 2, 2, 0
Dish.getPrice() 1, 2, 2, 1
Main.main() 2, 9, 3, 3
Order.addARecord() 3, 7, 3, 2
Order.getTotalPrice() 2, 5, 3, 0

--------------------------------------------------------------------------------------------
Block Depth Statements

0 9
1 15
2 18
3 7
4 0
5 0
6 0
7 0
8 0
9+ 0

这是菜单类的第一大题,该题涉及到了类与类之间的属性方法的相互调用,该题我一共创建了五个类:主类、菜品类、菜谱类、点菜记录类、订单类;在菜品类中有菜名属性name和菜品单价unit_price和getPrice方法该方法中需传参,将菜品的份额portion传入该方法中,在该方法中用switch语句实现对每一道菜计算价格,在菜单类我创建了菜品数组用来保存所有菜品信息,并在该菜单类中的Dish searthDish(String dishName)方法中对菜单中的菜品信息(菜名、单价)做了初始化操作并且设置了比对功能,如果顾客所点的菜与菜单中的菜不匹配则print该菜does not exist,若匹配成功则返回顾客所点菜的菜名。在点菜记录类中我也创建了菜品类Dish的对象和份额portion属性,该订单中的getPrice()方法与Dish类重名,可是两者内容是不同的,Record类中的getPrice方法是用来计价计算本条记录的价格。订单类中创建了点菜记录类Record的对象,并创建了getTotalPrice()方法用来计算点菜记录的总价,也创建了addARecord(String dishName,int portion)的方法用来实现不同顾客不同点菜记录的需求。在创建好这些必须类的内容后,我们再开始主类内容,该题输入主要是菜名和份额,也就是输入字符型和整形数据,最后输出菜的总价或某某菜不存在,所以创建字符变量a接收输入的字符型数据,整形变量b来接收输入菜名的份额,并创建order类与Menu类对象分别为c和n,只要变量a接收的数据不是end,b变量接收份额,并且c变量也继续接收储存新的点菜记录,这之后还要运用Menu对象n中的方法searthDish(String dishName)与点菜记录Record比对,存在则输出总价,若有不存在的菜名,则输出dishname+does not exist和总价。

7-4:小明走格子

 代码如下:

import java.io.*;
import java.util.*;
public class Main {
public static void main(String args[]) throws IOException{
StreamTokenizer re = new StreamTokenizer(new BufferedReader(new InputStreamReader(System.in)));
re.nextToken();
int R_xiaohui = (int)re.nval;
int m_xiaohui[] = new int[10010];
int n_xiaohui[] = new int[100];
int i,j,max_xiaohui = 0;
for(i=0;i<R_xiaohui;i++)
{
re.nextToken();
m_xiaohui[i] = (int)re.nval;
if(max_xiaohui < m_xiaohui[i])
max_xiaohui = m_xiaohui[i];
}
n_xiaohui[0] = 1;
n_xiaohui[1] = 1;
n_xiaohui[2] = 2;
n_xiaohui[3] = 4;
n_xiaohui[4] = 8;
for(i=5;i<=max_xiaohui;i++) n_xiaohui[i] = 2 * n_xiaohui[i - 1] - n_xiaohui[i - 5];
for(i=0;i<R_xiaohui;i++)
System.out.println(n_xiaohui[m_xiaohui[i]]);
}
}

这道题目是一道经典的算法题,考到的知识点是动态规划,只需要懂一些这方面的算法即可轻松解题。

3、第三次作业:

 

 除了菜单计价程序,其他的都拿了满分,说明确实菜单程序比较难,逻辑复杂,类与类之间的关系复杂。

该题又是在前面两题的基础上进行改进升级的,该题增加了点单时间及对应时间打折或判断是否是营业时间,增加了桌号,以及代点代付功能。

对于代点代付功能其本质还是点的那一桌付钱记录的,所以只需要在record类中添加table属性来判断点餐桌号就行,到时候通过这个桌号知道是代点的还是为自己点的

 

 

 对于增加的点单时间及对应时间打折或判断是否是营业时间和桌号,我为此增加了Time类来处理时间,Table类来处理桌号问题,Table类中添加Time类属性来记录点菜时间及相关处理,增加Order类属性来记录该桌的点菜记录

其中用了of方法记录时间getime方法存入数据

 

 

 

目前来看功能好像是完成了,但main类也要做出相应的改变,然后发现此时再将menu类中的i设为静态变量会出问题了,于是我将menu类代码做了如下调整

将i的下标调到table类中的ordernum

 

 相应的main类代码更改如下

 

 

 

 至此该三次作业完毕。

三、踩坑心得:

  1. 运行超时!!

   为什么把运行超时放在第一个说呢,当我的代码在eclipse上输入样例全都可以过时, 我两眼放光的把代码再复制到pta中去过测试点——恭喜你得了1分,原因是运行超时(震怒。然鹅虽然“运行超时”这个测试点很让人揪心,但实际上,我也通过这个学会了很多新东西。那么,来看看运行超时教会我什么吧。

            ①第二次作业7-4小明走格子

                                

                 通过学习字符流输入(BufferedReader),提升输入速度,从而减小运行时间

               

 

              ②第三次作业7-3去掉重复的数据

        

 

          在写代码时,最重要的是规划,如果一个for循环就可以解决的问题,不必用更多的for循环而导致运行超时。

                ③第三次作业7-1菜单计价程序-3

                 

      records[],menu[],初始化定义的大小过大(1000多),我猜是因为这个导致运行超时,经过查资料发现,动态数组arraylist可以不用一开始固定数组大小,可以用其add方法增项。用arraylist后就可以过测试点了。

    2.非零返回与空指针异常

         ①java报错空指针异常:如果一个对象本身为空,我们使用对象本身的时候不会出现异常,因为他是有值的,值就是null。可如果我们进一步项直接使用对象内的成员进行操作,按系统就会报空指针异常,因为对象是空的,他不存在成员,那么直接去访问这个车管员,当然会出错。所以在写方法时不要遗漏return null。

  ②非零返回:我大部分的非零返回原因都是程序的语法在PTA执行的过程中抛出了异常,导致程序没能运行到return,就导致了非零返回。把代码从头到尾看一遍,仔细阅读会发现,在调 用某些类的方法的时候,如果这些方法可能会返回null,应该在调用时判断返回值是否为空,再进行下面的操作,这样说可能不够清晰,下面来看代码示例。

                                                        

这是在Order类里的删除一条订单信息的方法,这里调用了records[]属性,如果在循环中不加if(records[i]!=null)的判断条件的话,会报非零返回的错,原因是records数组中的项都是通过add方法加进来的,当不满足某个条件时,add方法会返回null,如果满足所有条件,add会返回已经添加到records里的项。也就是说如果在该段删除方法中不加这条判断是否为空的话,如果records[[i]是null,那么在逻辑上是有漏洞的,没法向下进行到return,从而报非零返回的错。

四、主要困难以及改进建议:

  在解题过程中我会由于对知识点的不熟悉导致无从下手,得反复翻看Java书本内容才能上手,尤其是7-1 菜单计价程序-1、7-2 菜单计价程序-2、菜单计价程序-3,在类与类之间属性与方法之间的相互调用会让我弄不清,例如:

 

class Menu{

 

Dish[] dishs = new Dish(); //菜品数组,保存所有菜品信息 int t = 0;//统计录入的菜的数量 Dish searthDish(String dishName){//根据菜名在菜谱中查找菜品信息,返回Dish对象。
int flag = 0;
int i;
for(i=t-1;i>=0;i--){
if(dishName.equals(dishs[i].name)==true){
flag = 1;
break;
}
}
if(flag==1)
return dishs[i];
else
{
return null;
}
}
Dish addDish(String dishName,int unit_price)
{//添加一道菜品信息
//int j=0;

 

Dish dish1 =new Dish();
dish1.name = dishName;
dish1.unit_price = unit_price;
t++;
return dish1;
}
}

在Menu类中还有Dish类的对象,并且该菜品对象还是用数组来表示更让我头疼,我认为还是我对Java本质面向对象性了解的不深入,无法灵活地理清楚各个类之间的关系,每个类中属性、方法存在的意义。

改进建议:我们可以直接在Menu类中创建Dish[] dishs = new Dish[10];再int i=0;这样每个菜dish中的属性方法都储存于Dish[10]数组中,用i++来录入

五、总结: 

收获:

 

①学会了数组和String的很多方法,例如split,charat,length,equls等。

 

②写出了比较有意思的方法,如isNum(判断字符串是否全为数字)。

 

③学习了动态数组arraylist的使用和其方法(如sort,set get,add等方法)。

 

④学会了输入流来缩短运行时长。

 

⑤了解认识了Calendar类和Date类。

 

仍需改进:

 

因为有过使用Calendar失败的经历,所以更需要去深刻了解该类的使用去改进代码的不足。

 

还要就是应该多查些资料看些比较优秀的java代码,从而对自己的面向对象思维进行合理建构。

 

课业改进意见:

 

  对于后两次作业,比较困难的点其实是我太菜了,要是想把这种对于我来讲比较难的题目在短短一周(还要完成其他课业)的情况下之下做完,实在是太太太太痛苦啦,有一种刚出生就让我说八国语言的压迫感(bushi。改进建议是希望老师不要出题出的难度跨度太大。

 

 

posted on 2023-05-23 17:41  aidaimade  阅读(150)  评论(0)    收藏  举报