第二次博客:PTA题目集4-5总结

mu-yu-hua / 2023-05-13 / 原文

第二次博客:PTA题目集4-5总结

 

   前言:有了前三次的菜单系列的洗礼,我对这两次的点菜还是有一定的信心的。老师也说期中考试会于点菜系列有一定的联系。但是事实告诉你永远不要试图揣测蔡老师的心思。

  先说期中考试:

题目概述:

  总共4题,(和点菜没有半毛钱关系)不过总体来说不难。

  第一题:设计一个圆类,要求输入半径,若半径有误则输出“wornformat”,否则输出面积。

并没有什么困难,只要会用Math.PI,就可以了。

 

  第二题:设计一个矩形类,以及点类,矩形由两个点组成(左上,右下),只需要输出面积,唯一麻烦的是所有的属性都是prevate类型的都要所有对应的set和get方法进行操作,以及要写新的构造方法(在构造时将属性全部设置好)。

    第三题:将前两题结合,写一个图形类作为圆和矩形的父类,由于有类图所以并没有什么困难,唯一可能有点的就是第一个测试点,谁能想到圆的半径为0是是错误输入

    第四题:没有什么新鲜的内容就是要求对set进行排序。下面是我的图形类

abstract class Shape implements Comparable

{

public double getAres;

Shape(){}

public double getAres() {

// TODO Auto-generated method stub

return 0;

};

public boolean chack;

public boolean chack() {

return true;

}

@Override

public int compareTo(Object o) {

// TODO Auto-generated method stub

Shape s = (Shape)o;

if(this.getAres()>s.getAres()){

return 1;

}else if(this.getAres()<s.getAres()){

return -1;

}

return 0;

}

}

 

  接下来将开始讲解两次点菜,这两次的点菜并没有什么联系,都是点菜3的两个不同的分支情况,4只要为对错误输入的处理,5主要是添加新的功能。只能说老师就是老师花活就是多。

  点菜4

  由于代码是拿点菜3改的,所以很多人可能会在主方法内堆,这样子会失去JAVA的本质(面向对象),所以为了JAVA的灵魂

我设计了一个Worn类,里面的worninput方法使用了正则表达式,对输入进行判断输入是否正确,将输入全存入String[]a里面以及一个int[]b数组进行标记具体代码如下

 

   

class Worn//错误判断,错误b[i]==1
{
void worninput(String[]a,int num,int[]b)
{
//boolean flag1=true;

for(int i=0;i<num;i++)
{
String[] arr=a[i].split(" ");
boolean flag=false;
//麻婆豆腐 12
if(arr.length>5)
{
b[i]=1;
}
if(a[i].matches("^[\u4e00-\u9fa5]{1,} ([1-9][0-9]{0,2})$")==true)//普通菜存入
{
flag=true;
}
//油淋生菜 9 T
else if(a[i].matches("^[\u4e00-\u9fa5]{1,4} ([1-9][0-9]{0,2}) [T]$")==true)//特色菜存入
{
flag=true;
}
//table 31 2023/2/1 14/20/00
else if(arr[0].equals("table")==true)//桌
{
int blank=0;
for(int bk=0;bk<a[i].length();bk++)
{
if(a[i].charAt(bk)==' ')
{
blank++;
}
}
if(arr[1].matches("^([1-9][0-9]{0,})$")&&blank==3)
flag=true;
//int blank=0;

}
//1 麻婆豆腐 1 16
else if(a[i].matches("^[1-9][0-9]{0,2} [\u4e00-\u9fa5]{1,} [1-9] ([1-9][0-9]{0,1})$")==true)//自己点菜
{
flag=true;
}
//1 1 麻婆豆腐 1 16
else if(a[i].matches("^([1-9][0-9]{0,1}) [1-9][0-9]{0,2} [\u4e00-\u9fa5]{1,} [1-9] ([1-9][0-9]{0,1})$")==true)//待点菜
{
flag=true;
}
//2 delete
else if(a[i].matches("^([1-9][0-9]{0,1}) delete$")==true)//删菜
{
flag=true;
}
else if(a[i].matches("^end$")==true)//结尾
{
flag=true;
}
if(flag==false)
{
b[i]=1;
}
}
}
}


  下一个难点就是,如果两桌的桌号相同且在同一时间段,要被认为是同一桌,所以为了解决这个问题,每次处理一个新的桌次信息的时候我会先执行一个sarchtable方法返回次桌在数组中的位置,若没有这会返回一个新的位置(其实可以直接用Map解决,当时没有想到)

 

int searchtable(table[]t,table a,int sumtable)//若找到返回对应桌,找不到返回下一个空桌
{
//(时段的认定:周一到周五的中午或晚上是同一时段,或者周末时间间隔1小时(不含一小时整,精确到秒)
for(int i=0;i<sumtable;i++)
{
if(a.tablenum==t[i].tablenum)
if(a.year==t[i].year)
{
if(a.day==t[i].day)
{
if(a.ww>=1&&a.ww<=5)//周1~5
{
if(a.hh<=14&&t[i].hh<=14||a.hh>14&&t[i].hh>14)//在同一时段
{

return i;

}
}
else//周末
{
if(Math.abs((a.hh-t[i].hh)*60*60+(a.mm-t[i].mm)*60)+(a.ss-t[i].ss)<60*60)//在同一时段
{

return i;
}

}
}
}
}
//System.out.println(sumtable);
return sumtable;
}

 

 

  之后就是要处理若是桌次信息出现问题要忽略本桌信息,若是使用的边输入边输出的方法(输入一条处理一条),可以直接continue,而我是无论桌次信息是否错误都会开一个新的桌,之后在输出的时候判断此桌是否有问题,进而选择输出与否。下面是我的判断

boolean judgetime1()//是否有效//使用正则表达式
{
String[] arr=time.split(" ");
boolean flag=arr[0].matches("^(2022|2023)/(([13578]|0[13578]|10|12)/(([1-9]|0[1-9])|([12][0-9])|3[01])|([469]|0[469]|11)/(([1-9]|0[1-9])|([12][0-9])|30)|(2|02)/(([1-9]|0[1-9])|(1[0-9])|(2[0-8])))$");
boolean flag1=arr[1].matches("^([0-9]|([01][0-9])|(2[0-3]))/([0-9]|([0-5][0-9]))/([0-9]|([0-5][0-9]))$");
return (flag&&flag1);

}

 

这样子基本所有的问题都解决了。但是还是要吐槽一下有些测试点真的阴间。

 

  点菜5

  这次主要是加入了客户的概念,将同一客户名下的所有桌的价钱合并,以及加入口味值,在输出桌总价的时候要输出本桌的口味平均值。

  对于人这个概念,

  1.可以选择直接在table类中加入一个属性,并在算总价的时候对名字相同的桌的价格进行合并。

  2.可以创立一个客户类,里面设立一个table类的数组。

  对于1 其更像面向过程,对于2,它的代点菜会相当困难(要对所有的客户的所有桌进行遍历),所以我引入了Data类,存放所有数据,并将所有的属性都设为static

 

class Data
{
static Map<String,man> allman = new TreeMap<>(new Comparator<String>() {
@Override
public int compare(String o1, String o2) {
return o1.compareTo(o2);
}
});

static Map<Integer,table>t=new HashMap<>();
static Menu c=new Menu();
}

 

客户和桌都用Map进行储存方便查找,并从写客户的排序比较规则,见上文。

  在客户类中加入一个int数组用于储存其拥有的桌号,在算总价的时候,直接通过Map访问对应的桌,的到对应的总价。

class man
{
String name;
String phone_number;
int sum=0;
List<Integer>tablenum=new ArrayList<>();
// table 1 : tom 13605054400 2023/5/6 12/30/00
//约束条件:客户姓名不超过10个字符,
//手机号11位,前三位必须是180、181、189、133、135、136其中之一。
static void newman(String x)
{
//分割
String[] arr=x.split(" ");
man m=new man();
if(arr[3].matches("^.{1,10}$")&&arr[4].matches("^(180|181|189|133|135|136)[0-9]{8}$"))
{
m.name=arr[3];
m.phone_number=arr[4];
if(!Data.allman.containsKey(m.name))//没有,加人
Data.allman.put(m.name,m);
Data.allman.get(m.name).addtable(table.n);//添加桌号
}
else
{
System.out.println("wrong format");
}
}

//table 1 : tom 13605054400 2023/5/1 18/30/00
void addtable(int n)
{
tablenum.add(n);
}
void getttprice()
{
Iterator it =tablenum.iterator();
while(it.hasNext())
{
sum+=Data.t.get(it.next()).truesum;
}
System.out.println(name+" "+phone_number+" "+sum);
}
static void show()
{
Set<Entry<String, man>> entrySet = Data.allman.entrySet();
for(Map.Entry<String,man> entry : entrySet){
entry.getValue().getttprice();
}
}
}

 

  对于口味值,我在桌类中加入了一个额外的Order,eatorder用于计算口味值,buyorder用于计算总价,自己点菜时直接加入的到两个order中,带点菜的时候,将订单加入自己的buyorder以及被带点桌的eatorder

class table
{
int tablenum;//桌号
String time;//点菜时间
int year=0,month=0,day=0,ww=0,hh=0,mm=0,ss=0;
boolean flag=true;//判断时间是否正确
double count=0,specialcount=0;//折扣
Order eatorder=new Order();//吃的订单
Order buyorder=new Order();//点的订单
int sum=0,truesum1=0;//计算总价
double truesum=0;
int a1=0,b1=0,c1=0;//记录菜系对应菜分数
int a2=0,b2=0,c2=0;//总辣栓甜度
static int n=0;//记录当前处理桌号
static boolean newtable(String a)
{
table t=new table();
t.input(a);
if(t.flag)
{
Data.t.put(t.tablenum, t);
n=t.tablenum;
return true;
}
return false;
}
void input(String time)//预处理
{
this.time=time;
timechange();
jscount();
pdflag();
}
void getTotalkoweidu()
{

for(Record it:eatorder.records){

if(it.d.special)
{
if(it.d.菜系==1)
{
int y=it.num;
a1+=y;
a2+=it.口味*y;
}
else if(it.d.菜系==2)
{
int y=it.num;
b1+=y;
b2+=it.口味*y;
}
else if(it.d.菜系==3)
{
int y=it.num;
c1+=y;
c2+=it.口味*y;
}
}
}

if(a1==0&&b1==0&&c1==0)
System.out.print(" ");
if(a1!=0)
{
a2=(int)(a2*1.0/a1+0.5);
System.out.print(" 川菜 "+a1);
switch(a2)
{
case 0:System.out.print(" 不辣");break;
case 1:System.out.print(" 微辣");break;
case 2:System.out.print(" 稍辣");break;
case 3:System.out.print(" 辣");break;
case 4:System.out.print(" 很辣");break;
case 5:System.out.print(" 爆辣");break;

}
}
if(b1!=0)
{
b2=(int)(b2*1.0/b1+0.5);
System.out.print(" 晋菜 "+b1);
switch(b2)
{
case 0:System.out.print(" 不酸");break;
case 1:System.out.print(" 微酸");break;
case 2:System.out.print(" 稍酸");break;
case 3:System.out.print(" 酸");break;
case 4:System.out.print(" 很酸");break;

}
}
if(c1!=0)
{
c2=(int)(c2*1.0/c1+0.5);
System.out.print(" 浙菜 "+c1);
switch(c2)
{
case 0:System.out.print(" 不甜");break;
case 1:System.out.print(" 微甜");break;
case 2:System.out.print(" 稍甜");break;
case 3:System.out.print(" 甜"); break;
}

}
System.out.print("\n");
}

void getTotalPrice()//计算桌总价
{
if(flag)
{
for(Record it:buyorder.records){
int n=it.price;
sum+=n;
if(it.d.special)
truesum+=(int)(n*specialcount+0.5);
else
truesum+=(int)(n*count+0.5);
}
truesum1=(int)(truesum+0.5);
System.out.print("table "+tablenum+": "+sum+" "+truesum1);
}
}
void jscount()//运用时间计算折扣
{
if(ww>=1&&ww<=5)
{
specialcount=0.7;
if(hh>=17&&hh<20)
count=0.8;
else if(hh==20&&mm<30)
count=0.8;
else if(hh==20&&mm==30&&ss==0)
count=0.8;
else if(hh>=11&&hh<=13||hh==10&&mm>=30)
count=0.6;
else if(hh==14&&mm<30)
count=0.6;
else if(hh==14&&mm==30&&ss==0)
count=0.6;
}
else
{
specialcount=1.0;
if(hh>=10&&hh<=20)
count=1.0;
else if(hh==9&&mm>=30)
count=1.0;
else if(hh==21&&mm<30||hh==21&&mm==30&&ss==0)
count=1.0;
}
}
void pdflag()//判断时间是否正确
{
if(count==0)
{
System.out.println("table "+tablenum+" out of opening hours");
flag=false;
}
else
{
System.out.println("table "+tablenum+": ");
flag=true;
}
}
void timechange()//时间转换
{
String[] arr1=time.split(" ");
tablenum=Integer.parseInt(arr1[1]);//桌号
String[] arr2=arr1[2].split("/");
String[] arr3=arr1[3].split("/");
year=Integer.parseInt(arr2[0]);//时间
month=Integer.parseInt(arr2[1]);
day=Integer.parseInt(arr2[2]);
Calendar c = Calendar.getInstance();
c.set(year,month-1,day);//设置时间
ww=c.get(Calendar.DAY_OF_WEEK);
hh=Integer.parseInt(arr3[0]);//时间
mm=Integer.parseInt(arr3[1]);
ss=Integer.parseInt(arr3[2]);
if(ww==1)
ww=7;
else
ww--;
}
static void show()
{
Set<Entry<Integer,table>> entrySet = Data.t.entrySet();
for(Map.Entry<Integer,table> entry : entrySet){
entry.getValue().getTotalPrice();
entry.getValue().getTotalkoweidu();
}
}

}

 

  我顺便用将menu类的dishs改为Map,感觉一下子就高端起来了

 

class Menu
{
Map <String,Dish>dishs =new HashMap<>();

int i=0;//用于记录菜品总数

void addDish(String a)
{
String[] arr=a.split(" ");
Dish d=new Dish();
d.name=arr[0];
if(arr.length==4)//特色菜
{
d.special=true;
d.unit_price=Integer.parseInt(arr[2]);
if(arr[1].equals("川菜"))
d.菜系=1;
else if(arr[1].equals("晋菜"))
d.菜系=2;
else if(arr[1].equals("浙菜"))
d.菜系=3;
}

else//普通菜
{
d.unit_price=Integer.parseInt(arr[1]);
}
dishs.put(d.name,d);
}
Dish searthDish(String a,boolean flag1) //根据菜名在菜谱中查找菜品信息,返回Dish对象。
{
String dishName;
String[] arr=a.split(" ");
Dish d=new Dish();
if(arr[1].matches("^[0-9]{1,}$"))
{
dishName=arr[2];
}
else
dishName=arr[1];
if(dishs.containsKey(dishName))
{
d=dishs.get(dishName);
if(d.special&&arr.length==5)
{
int 度=0;
if(arr[1].matches("^[0-9]{1,}$"))
{
度=Integer.parseInt(arr[3]);
}
else
{
度=Integer.parseInt(arr[2]);
}
if(d.菜系==1)
{
if(度!=0&&度!=1&&度!=2&&度!=3&&度!=4&&度!=5)
{
if(flag1)
System.out.println("spicy num out of range :"+度);
return null;
}
}
else if(d.菜系==2)
{
if(度!=0&&度!=1&&度!=2&&度!=3&&度!=4)
{
if(flag1)
System.out.println("acidity num out of range :"+度);
return null;
}
}
else if(d.菜系==3)
{
if(度!=0&&度!=1&&度!=2&&度!=3)
{
if(flag1)
System.out.println("sweetness num out of range :"+度);
return null;
}
}
}
return d;
}
else
{
if(flag1)
System.out.println(dishName+" does not exist");
return null;
}
}
}

 

 

 

菜单系列踩坑心得(这世上本没有坑,踩的人多了就成了坑)

    1.可以去elicpice上测试,但是搞完复制回PTA一定一定要把package删了,以及主类改为Main,会非零返回。

    2.使用数组元素时一定要先new

    3.对象第一次new了之后,无论在那都不要再new了不然会变成null

    4.访问数组不能越界,会有段错误

    5.只要不是void类型的方法一定要保证所有情况都有返回值,会非零返回

    6.不能在定义属性的地方使用方法,如7-1中对菜单的初始化

    7.一些超时可能是圈复杂度过高,或循环没有出口下面是我的第三个菜单的圈复杂度

  

 

 

 

 

总结:

  通过这两次PTA大作业,我深刻感受到了,面向对象与面向过程的差别,对类有了一定的认识,题目都不算特别难,但是每一个题目都是一个知识点,特别是菜单系列刚开始的时候无从下手做完后再看看感慨万分,虽然类是老师设计的,但是其中的交流是自己打的,熬了几天大夜虽然很累但是拿满分的那一刻成就感满满。

学到了什么:

  1.对类的设计有了一定的了解。

  2.对集合有了一定的认知。

  3.体会到优秀的框架设计的好处(优秀的架构与细节设计。优秀的含义很广泛,清晰、可读、易维护、易扩展的设计往往是鲁棒的,这样的设计往往很难出现严重的纰漏,因为很多错误已经被规避掉了,即使有错误,发现、纠正的难度也不会太大

  4.对JAVA报错的修改更加得心应手。

  5.对类的封装性有了概念

   6.学习了正则表达式的基本使用

  7.合理使用各种泛型可以大大缩减代码量,而且非常高端

 

 对课程的建议

       1.每次作业截止之后可以出一下不计分的补题,可以让没有及时解决问题的同学继续尝试。