一,前言
第一次题目集偏基础,虽然咋一看题目量挺多的,有7题,但都没有难题,主要是Java中的一些基础知识(输入输出和怎么写最基本的Java程序),题目简单。
第二次,题目开始出现菜单计价程序1和菜单计价程序2,还有一道练习Java日期类的题目和一道小明走格子的算法题,题目开始与第一次作业不同,虽然题目少,但是每题都需要不少时间去写,尤其是菜单计价程序,刚开始还以为与第一次题目一样简单,结果难度突然加大,涉及到类的创建与调用方法重载的实现方式,输入快读方法等知识点。
第三次题目集中搭配了菜单计价程序3和其余6道小题目,菜单计价程序3是比较难的,需求较前两次多,难度自然更大,但其余6道题目均比较简单,花点时间就可以做出来。
二,设计与分析
主要分析菜单计价程序1,菜单计价程序2和菜单计价程序3。但是我还是先分析一下其它一些有意思的题目。
Java日期类题目:
刚开始没有仔细看题目,不知道Java之身带有日期类,还停留在c语言的思维上,开始自己写一个面向过程的程序,程序写的很繁琐,测试点还过不了。百思不得其解时询问了已经写出来的一个同学,才知道可以用Java自带的日期类写这道题,代码既简洁又符合面向对象设计(与之前不用Java日期类对比)。
具体思路是这样的,先判断输入的日期是否符合格式,再用format.parse将字符串解析为特定格式的日期或时间,判断是否符合逻辑,再将判断是否为闰年,然后根据日期输出题目要求,判断两个日期相隔天数方法相同,这是再判断这两个日期先后是否输入错误。
输入代码如下:
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.Scanner;
public class Main {
public static void main(String[] args) throws ParseException {
Scanner scan = new Scanner(System.in);
String dateStr = scan.nextLine();
int flag=1;
// 首先检查第一行日期字符串是否合法
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd");
Date date = null;
loop:
try {
format.setLenient(false); // 关闭严格检查
date = format.parse(dateStr);
} catch (ParseException e) {
System.out.println(dateStr + "无效!");
flag=0;
break loop;
}
// 检查年份是否为闰年
if(flag==1){
int year = Integer.parseInt(dateStr.substring(0,4));
boolean isLeap = false;
if (year % 4 == 0 && year % 100 != 0 || year % 400 == 0) {
isLeap = true;
System.out.println(dateStr + "是闰年.");
}
// 计算该日期是当年、当月、当周的第几天
int dayOfYear = end.get(Calendar.DAY_OF_YEAR); // 当年第几天
end.set(Calendar.DAY_OF_MONTH, 1);
int dayOfMonth = end.getActualMaximum(Calendar.DAY_OF_MONTH) - end.get(Calendar.DAY_OF_MONTH) + 2; // 当月第几天
int dayOfWeek = end.get(Calendar.DAY_OF_WEEK); // 当周第几天
System.out.printf("%s是当年第%d天,当月第%d天,当周第%d天.", endDateStr, dayOfYear, dayOfMonth, dayOfWeek);
}
String startDateStr = scan.next();
String endDateStr = scan.next();
// 将日期字符串转化为Date类型
Date startDate = null;
Date endDate = null;
try {
startDate = format.parse(startDateStr);
endDate = format.parse(endDateStr);
} catch (ParseException e) {
System.out.println(startDateStr + "或" + endDateStr + "中有不合法的日期.");
return;
}
// 判断结束日期是否早于开始日期
if (endDate.before(startDate)) {
System.out.println(endDateStr + "早于" + startDateStr + ",不合法!");
return;
}
// 计算相差天数、月数、年数
Calendar start = Calendar.getInstance();
start.setTime(startDate);
int startDayOfYear = start.get(Calendar.DAY_OF_YEAR); // 开始日期是当年的第几天
Calendar end = Calendar.getInstance();
end.setTime(endDate);
int endDayOfYear = end.get(Calendar.DAY_OF_YEAR); // 结束日期是当年的第几天
int diffDays = (int) ((endDate.getTime() - startDate.getTime()) / (24 * 60 * 60 * 1000)); // 相差天数
int diffMonths = (end.get(Calendar.YEAR) - start.get(Calendar.YEAR)) * 12 + end.get(Calendar.MONTH) - start.get(Calendar.MONTH); // 相差月数
int diffYears = end.get(Calendar.YEAR) - start.get(Calendar.YEAR); // 相差年数
// 输出开始和结束日期之间相差的天数、月数、年数
System.out.printf("%s与%s之间相差%d天,所在月份相差%d,所在年份相差%d.\n", startDateStr, endDateStr,
diffDays, diffMonths, diffYears);
}
}
首先看到这个题目,是比较蒙的,因为开始根本无从下手,不知到怎么写,还是Java老师在课堂上讲解了一下他的思路才茅舍顿开,原来是这样写的,菜单计价程序的代码实现了一个菜单和订单管理系统。其中,Dish类表示菜品信息,包括菜品名称和价格,Menu类维护了所有的菜品信息,并提供了查询单个菜品的接口,Record类表示订单中的一道菜品及其份数,Order类表示整个订单,包括多个菜品及其数量,可以添加、查询订单总价等等功能。,main函数通过调用这4个类来完成相应功能,具体思路是先创建一个菜谱对象和一个订单对象(订单对象用来保存订单,菜谱对象里已经保存好了菜谱),进入一个循环,接受的一整行的输入,判断输入为end时退出循环,将这一行沿空格进行分割,将相应的份数转换为浮点数,再将点菜的菜名与菜谱对象进行比较,检查是否有这个菜,有则记录在订单对象中(添加到订单对象的同时输出该菜与该菜的价钱,并将该价钱加入到总价里)没有则输出name + " does not exist",全部判断完毕后,输出总价钱。
代码如下:
import java.util.Scanner; public class Main { public static void main(String[] args) { Menu menu = new Menu(); Order order = new Order(menu); Scanner scanner = new Scanner(System.in); while (true) { String input = scanner.nextLine(); if (input.equals("end")) { break; } String[] split = input.split(" "); String name = split[0]; int portion = Integer.parseInt(split[1]); Record record = order.addARecord(name, portion); if (record == null) { System.out.println(name + " does not exist"); } } System.out.println(order.getTotalPrice()); // for (Record record : order.records) { // System.out.println( record.getPrice() * record.portion); // } } } class Dish { String name; int unit_price; public Dish(String name, int unit_price) { this.name = name; this.unit_price = unit_price; } int getPrice(int portion) { double price = 0; switch (portion) { case 1: price = unit_price; break; case 2: price = unit_price * 1.5; break; case 3: price = unit_price * 2; break; } return (int) Math.round(price); } } class Menu { Dish[] dishes; public Menu() { dishes = new Dish[]{ new Dish("西红柿炒蛋", 15), new Dish("清炒土豆丝", 12), new Dish("麻婆豆腐", 12), new Dish("油淋生菜", 9) }; } Dish searchDish(String dishName) { for (Dish dish : dishes) { if (dish.name.equals(dishName)) { return dish; } } return null; } } class Record { Dish d; int portion; public Record(Dish d, int portion) { this.d = d; this.portion = portion; } int getPrice() { return d.getPrice(portion); } } class Order { Record[] records; Menu menu; public Order(Menu menu) { this.menu = menu; this.records = new Record[0]; } Record addARecord(String dishName, int portion) { Dish dish = menu.searchDish(dishName); if (dish == null) { return null; } Record record = new Record(dish, portion); Record[] tmp = new Record[records.length + 1]; System.arraycopy(records, 0, tmp, 0, records.length); tmp[records.length] = record; records = tmp; return record; } int getTotalPrice() { int price = 0; for (Record record : records) { price += record.getPrice() ; } return price; } }
该程序为菜单计价程序-1的迭代版本,在菜单计价程序-1的基础上做了一些修改,输入顺序为先输入菜品,再点菜,end结束。并且加了一个delete删除功能和点菜的份额属性。
思路为具体思路是先创建一个菜谱对象和一个订单对象(订单对象用来保存订单,菜谱对象里已经保存好了菜谱),进入一个循环,接受的一整行的输入,判断输入为end时退出循环,如果是
将这一行沿空格进行分割,根据分割后的数组长度来区分点菜和输入菜品,如果是两个字符,那便存入dishs数组Dish类型,反之,则是点菜记录,将相应的份数转换为浮点数,再将点菜的菜名与菜谱对象进行比较,检查是否有这个菜,有则记录在订单对象中(添加到订单对象的同时输出该菜与该菜的价钱,
并将该价钱加入到总价里)没有则输出name + " does not exist",全部判断完毕后,输出总价钱。
代码入下:
public class Main{
public static void main(String[] args){
Scanner sc = new Scanner(System.in);
Menu mu = new Menu();
Order od = new Order();
int j=0,l=0;
Dish tt = null;
while(true){
int count = 0;
String st = sc.nextLine();
for(int i=0;i<st.length();i++){
String te = st.substring(i,i+1);
if(te.equals(" ")){
count++;
}
}
if(st.equals("end"))
break;
if(count==1){
String[] temp = st.split(" ");
if(temp[1].equals("delete")){
if(!od.delARecordByOrderNum(Integer.parseInt(temp[0]))){
System.out.println("delete error;");
}
}else{
mu.dishs[l] = mu.addDish(temp[0],Integer.parseInt(temp[1]));
l++;
}
}
if(count==3){
String[] temp1 = st.split(" ");
od.records[j]=od.addARecord(Integer.parseInt(temp1[0]),temp1[1],Integer.parseInt(temp1[2]),Integer.parseInt(temp1[3]));
tt = mu.searthDish(od.records[j].d.name);
if(tt==null){
System.out.println(od.records[j].d.name+" does not exist");
}else{
od.records[j].d.unit_price = tt.unit_price;
System.out.println(od.records[j].orderNum+" "+od.records[j].d.name+" "+od.records[j].d.getPrice(od.records[j].portion));
}
j++;
}
}
System.out.print(od.getTotalPrice());
}
}
class Dish {
String name;//菜品名称
int unit_price; //单价
int num;
int getPrice(int portion) {
int peic = 0;
switch (portion) {
case 1:
peic = (int)unit_price * num;
break;
case 2:
peic = Math.round((float) (unit_price * 1.5)) * num;
break;
case 3:
peic = (int) (unit_price * 2) * num;
break;
}
return peic;//计算菜品价格的方法,输入参数是点菜的份额(输入数据只能是1/2/3,代表小/中/大份)
}
}
class Menu {
Dish[] dishs = new Dish[10];//菜品数组,保存所有菜品信息
int count = 0;
Dish searthDish(String dishName){
Dish temd = null;
for(int i=count-1;i>=0;i--){
if(dishName.equals(dishs[i].name)){
temd = dishs[i];
break;
}
}
if(temd==null){
System.out.println(dishName+" does not exist");
}
return temd;
}//根据菜名在菜谱中查找菜品信息,返回Dish对象。
Dish addDish(String dishName,int unit_price){
Dish dh = new Dish();
dh.name = dishName;
dh.unit_price = unit_price;
count++;
return dh;
}//添加一道菜品信息
}
class Record {
int orderNum;//序号\
Dish d = new Dish();//菜品\
int portion;//份额(1/2/3代表小/中/大份)\
int exist = 1;
int getPrice(){
return d.getPrice(portion);
}//计价,计算本条记录的价格\
}
class Order {
Record[] records = new Record[1000];//保存订单上每一道的记录
int count = 0;
int getTotalPrice(){
int sum=0;
for(int i=0;i<count;i++){
if(records[i].exist==0)
continue;
sum=sum+records[i].getPrice();
}
return sum;
}//计算订单的总价
Record addARecord(int orderNum,String dishName,int portion,int num){
Record rd1 = new Record();
rd1.d.name = dishName;
rd1.orderNum = orderNum;
rd1.portion = portion;
rd1.d.num = num;
count++;
return rd1;
}//添加一条菜品信息到订单中。
boolean delARecordByOrderNum(int orderNum){
if(orderNum>count||orderNum<=0){
//System.out.println("delete error");
return false;
}else
records[orderNum-1].exist=0;
return true;
}//根据序号删除一条记录
void findRecordByNum(int orderNum){
}//根据序号查找一条记录
菜单计价程序-3
此程序为菜单计价程序-2的迭代版本,新增加了一个桌号标识还有时间参数用来计算折扣折扣的计算方法(注:以下时间段均按闭区间计算):
周一至周五营业时间与折扣:晚上(17:00-20:30)8折,周一至周五中午(10:30--14:30)6折,其余时间不营业。周末全价,营业时间:
9:30-21:30如果下单时间不在营业范围内,输出"table " + t.tableNum + " out of opening hours"
由于对菜单程序的疏忽导致到了规定时间内还没有写出正确的代码,导致这题没有拿到全部分数,不过思路与前面相同,不过新加了桌子这个类,还有计算时间的方法,
到目前我还没有写出完全正确的代码。
三,踩坑心得
1,写程序应该在编译软件上写好,再在pta上提交,毕竟pta无法报错,导致前期在pta上寻找错误花费了,很多时间。
2,在实现第三道题目中,最困难的是维护数量不确定的订单中的菜品,特别是在添加订单记录时需要动态地增加记录,在Java中可以使用数组实现。同时,在写查询单个菜品的接口时,
需要注意到可能返回null值的情况,必须进行判断以避免程序崩溃。除此之外,整个程序的设计并没有太大的问题,实现起来比较顺畅。
3,还有一个比较严重的问题就是运行超时,

这是菜单计价程序-3的错误,当时运用数组保存菜品和订单记录导致的,后来发现ArrayList可以降低运行时长,可以过测试点,
四, 主要困难以及改进建议:
在编程过程中,遇到的最主要的困难是如何动态地维护订单记录。初次尝试使用ArrayList实现,但是由于缺少经验,调试的过程中出现了许多语法和调用接口的错误,比较耗费时间和精力。后来改用数组实现,避免了该问题。对于这种情况建议以后可以更加认真地处理好数据结构的选择和数组动态增长的实现机制,在实现过程中及时进行调试和测试。
另外,在编程过程中还发现自己还有很多不足之处,如对Java类与对象的定义和使用不够熟练,函数命名语义不够准确等,需要加强学习和练习。
(5) 总结:
通过这三次题目集的学习,掌握了基本的输入输出、运算符、条件判断和循环结构的使用,学会了如何实现类的定义、构造函数、函数重载、数组、继承和封装,以及面向对象的编程思想和设计,对类之间的协作和关系有了更加深入的理解,
在以后的学习中,需要进一步加强对Java类与对象的理解和使用,掌握更多的编程技巧和设计模式,提高编程能力和效率。对于教师、课程、作业、实验、课上及课下组织方式等方面的改进建议及意见,建议加强实践操作与实际问题的解决能力、加强问题分析和解决能力,开展更有针对性的实践和探究活动。
