目录
一、前言
1.难度分析
此次博客围绕6-8次PTA题目集(电信计费),难度在电信计费的三道题目上,主要是内容太多了,让人摸不着头脑,题目其实并不难(相较于前面那几道多边形的题目),除此之外其他的几道送分题也会稍有提到注意点。
2.题量分析
比前几次大作业要少,除了送分题就三道电信计费。
3.知识点
6-7-1 电信计费系列1-座机计费
1、月租20元,接电话免费,市内拨打电话0.1元/分钟,省内长途0.3元/ 分钟,国内长途拨打0.6元/分钟。不足一分钟按一分钟计。
2、南昌市的区号:0791,江西省内各地市区号包括:0790~0799以及0701。
3、座机号码由区号和电话号码拼接而成,电话号码包含7-8位数字,区号最高位是0。座机号码有以下两种类型:
(1)区号:4位 电话号码:8位
(2)区号:3位 电话号码:7位
4、时间必须符合"yyyy.MM.dd HH:mm:ss"格式(如2022.1.3 10:00:25),建议使用SimpleDateFormat类编写代码。
5、每个用户初始余额是100元。计算所有已开户的用户的当月费用(精确到小数点后2位,单位元),没有开户的用户不能计算费用。
6、注意,题目提到不检查“开户号码非南昌市的号码”这种情况,也就是说用不是南昌的座机区号开通用户也是正确的(至少在这里)。除了存在4位的座机区号外,还有3位的座机区号(如北上广),区分是3位的号码长度是10位,也就是上面知识点3中的类型(2)。
7-7-1 电信计费系列2-手机+座机计费
除了座机计费的知识点或者说内容点以外,增加了“手机计费”的以下几点:
1、月租15元,市内省内接电话均免费,市内拨打市内电话0.1元/分钟,市内拨打省内电话0.2元/分钟,市内拨打省外电话0.3元/分钟,省内漫游打电话0.3元/分钟,省外漫游接听0.3元/分钟,省外漫游拨打0.6元/分钟。
个人理解:每个月要交15元月租,只有在省外接别人的电话才需要计费,市内和省内打电话都是0.3元/分钟,在省外打电话是0.6元/分钟。
2、被叫电话属于市内、省内还是国内由被叫电话的接听地点区号决定,比如以下案例中,南昌市手机用户13307912264在区号为020的广州接听了电话,主叫号码应被计算为拨打了一个省外长途,同时,手机用户13307912264也要被计算省外接听漫游费:
u-13307912264 1
t-079186330022 13307912264 020 2022.1.3 10:00:25 2022.1.3
10:05:11
个人理解:看代码说话,第一行代码表示在南昌(题目默认)开通手机用户13307912264,第二行代码表示手机用户(13307912264)在区号为020(广州)接听了来自南昌的座机(079186330022)的来电。在这种情景下南昌的座机(079186330022)要以省外拨打电话为计费标准,而手机用户(13307912264)就要以省外接听电话为计费标准,虽然手机用户是在南昌开通的,但是他漫游到了广州,对于南昌来说就是省外。
3、手机号码由11位数字构成,最高位是1。
8--1 电信计费系列3-短信计费
1、计费方式:接收短信免费,发送短信0.1元/条,超过3条0.2元/条,超过5条0.3元/条。如果一次发送短信的字符数量超过10个,按每10个字符一条短信进行计算。
个人理解:结合下面的计费代码来看,先计算有多少条短信,然后根据短信的条数计费。
double sumCost = 0;//计费总金额
int number = 0;//短信条数
//遍历所有的发送记录
for (MessageRecord m : userRecords.getSendMessageRecords()) {
//单条短信的字符数量
int length = m.getMessage().length();
if (length <= 10) {//字符不超过10个
number++;//算作一条
} else {//字符超过10个
number += length / 10;//先取十位数相加
if (length % 10 != 0) {//再看个位数,不为0加1
number++;
}
}
}
//按总的短信条数计费
if (number <= 3) {//0-3条0.1元/条
sumCost = number * 0.1;
} else if (number <= 5) {//3-5条0.2元/条
sumCost = 0.3 + 0.2 * (number - 3);
} else {//超过5条0.3元/条
sumCost = 0.7 + 0.3 * (number - 5);
}
return sumCost;
二、设计与分析
第6次大作业
6-7-1 电信计费系列 1-座机计费
源码
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Locale;
import java.util.Scanner;
import java.util.Date;
public class Main {
public static void main(String args[]){
Scanner input=new Scanner(System.in);
String s=input.nextLine();
ArrayList<User> users = new ArrayList<>();
inputDeal inputdeal=new inputDeal();
boolean flag;
while (!s.equals("end")){
if (1 == inputdeal.judge(s)) {
inputdeal.writeUser(users, s);
} else if (2 == inputdeal.judge(s)) {
inputdeal.writeRecord(users, s);
}
s=input.nextLine();
}
for(int i=0;i< users.size();i++){//冒泡排序
for(int j=0;j< users.size()-i-1;j++){
if(Double.parseDouble(users.get(j).getNumber()) > Double.parseDouble(users.get(j+1).getNumber())){
User tempu=users.get(j);
users.set(j,users.get(j+1));
users.set(j+1,tempu);
}
}
}
//System.out.println(users.size());
for(User u:users){
System.out.println(u.getNumber()+" "+Common.round(u.calCost(),2)+" "+Common.round(u.calBalance(),2));
}
}
}
class inputDeal{
public int judge(String s){
String []s1=s.split(" ");
if(s1.length==2&&(s.matches("[u]-[0][7]([9][0-9]|[0][1])[0-9]{7,8} [0-3]")|| s.matches("[u]-1[0-9]{10}\\s[1]")))
return 1;//开通用户
if(s1.length==6&&s.matches("^t\\-0\\d{9,11}\\s0\\d{9,11}((\\s\\d{4}\\.([1-9]|([1]{1}[0-2]{1}))\\.([1-9]|"
+ "([1-2]{1}[0-9]{1})|3[0-1])\\s(([0-1][0-9])|(2[0-3]))\\:([0-5][0-9])\\:([0-5][0-9])){2})")){
//System.out.print("a");
return 2;//通话记录
}
return 0;
}
public void writeUser(ArrayList<User> users, String s){
String []s1=s.split(" ");
String number=s1[0].substring(2);
boolean flag=false;
User newuser = new User();
for(User u:users){
if(u.getNumber().equals(number))
flag=true;//号码已经开过户
}
if(!flag){//没开过户的才登记
newuser.setNumber(number);
int mode=Integer.parseInt(s1[1]);
if(mode==0){//收费模式0:座机
newuser.setChargeMode(new LandlinePhoneCharging());
}
users.add(newuser);
}
}
public void writeRecord(ArrayList<User> users, String s){
String []s1=s.split(" ");
s1[0]=s1[0].replace("t-","");//将t-去掉
//初始化callRecord
CallRecord callRecord=new CallRecord(s1);
SimpleDateFormat format=new SimpleDateFormat("yyyy.MM.dd HH:mm:ss");
Date startTime,endTime;
try {
startTime = format.parse(s1[2]+" "+s1[3]);
endTime = format.parse(s1[4]+" "+s1[5]);
} catch (ParseException e) {
throw new RuntimeException(e);
}
callRecord.setCallingAddressAreaCode(s1[0].substring(0,4));
callRecord.setAnswerAddressAreaCode(s1[1].substring(0,4));
callRecord.setStartTime(startTime);
callRecord.setEndTime(endTime);
callRecord.setCallingNumber(s1[0]);
callRecord.setAnswerNumber(s1[1]);
//判断是否添加记录
User callu=null,answeru=null;
for(User u:users){
if(u.getNumber().equals(s1[0])){
callu = u;
}
if (u.getNumber().equals(s1[1])) {
answeru = u;
}
if (callu != null && answeru != null) {
break;
}
}
if (callu != null) {
if (callRecord.getCallType() == 1) {
callu.getUserRecords().addCallingInCityRecords(callRecord);
} else if (callRecord.getCallType() == 2) {
callu.getUserRecords().addCallingInProvinceRecords(callRecord);
} else {
callu.getUserRecords().addCallingInLandRecords(callRecord);
}
}
if (answeru != null) {
if (callRecord.getCallType() == 1) {
answeru.getUserRecords().addAnswerInCityRecords(callRecord);
} else if (callRecord.getCallType() == 2) {
answeru.getUserRecords().addAnswerInProvinceRecords(callRecord);
} else {
answeru.getUserRecords().addAnswerInLandRecords(callRecord);
}
}
}
}
class User {
private UserRecords userRecords;
private double balance;
private ChargeMode chargeMode;
private String number;
User(){
userRecords=new UserRecords();
balance=100;
}
public double calCost(){
return chargeMode.calCost(userRecords);
}
public double calBalance(){
return balance - chargeMode.getMonthlyRent() - chargeMode.calCost(userRecords);
}
public UserRecords getUserRecords() {
return userRecords;
}
public void setUserRecords(UserRecords userRecords) {
this.userRecords = userRecords;
}
public double getBalance() {
return balance;
}
public ChargeMode getChargeMode() {
return chargeMode;
}
public void setChargeMode(ChargeMode chargeMode) {
this.chargeMode = chargeMode;
}
public String getNumber() {
return number;
}
public void setNumber(String number) {
this.number = number;
}
}
abstract class ChargeMode{
protected ArrayList<ChargeRule> chargeRules;
ChargeMode(){
chargeRules=new ArrayList<>();
}
public ArrayList<ChargeRule> getChargeRules() {
return chargeRules;
}
public void setChargeRules(ArrayList<ChargeRule> chargeRules) {
this.chargeRules = chargeRules;
}
public abstract double calCost(UserRecords userRecords);
public abstract double getMonthlyRent();
}
class LandlinePhoneCharging extends ChargeMode{
private double monthlyRent;
LandlinePhoneCharging(){
monthlyRent=20;
chargeRules.add(new LandPhoneInCityRule());
chargeRules.add(new LandPhoneInProvinceRule());
chargeRules.add(new LandPhoneInlandRule());
}
@Override
public double calCost(UserRecords userRecords){
double sumCost=0;
for(ChargeRule rule:chargeRules){
sumCost+=rule.calCost(userRecords);
}
return sumCost;
}
@Override
public double getMonthlyRent(){
return monthlyRent;
}
}
class UserRecords {
private ArrayList<CallRecord> callingInCityRecords;
private ArrayList<CallRecord> callingInProvinceRecords;
private ArrayList<CallRecord> callingInLandRecords;
private ArrayList<CallRecord> answerInCityRecords;
private ArrayList<CallRecord> answerInProvinceRecords;
private ArrayList<CallRecord> answerInLandRecords;
private ArrayList<MessageRecord> sendMessageRecords;
private ArrayList<MessageRecord> receiveMessageRecords;
UserRecords(){
callingInCityRecords=new ArrayList<CallRecord>();
callingInProvinceRecords=new ArrayList<CallRecord>();
callingInLandRecords=new ArrayList<CallRecord>();
answerInCityRecords=new ArrayList<CallRecord>();
answerInProvinceRecords=new ArrayList<CallRecord>();
answerInLandRecords=new ArrayList<CallRecord>();
sendMessageRecords=new ArrayList<MessageRecord>();
receiveMessageRecords=new ArrayList<MessageRecord>();
}
public void addCallingInCityRecords(CallRecord callRecord){
callingInCityRecords.add(callRecord);
}
public void addCallingInProvinceRecords(CallRecord callRecord){
callingInProvinceRecords.add(callRecord);
}
public void addCallingInLandRecords(CallRecord callRecord){
callingInLandRecords.add(callRecord);
}
public void addAnswerInCityRecords(CallRecord answerRecord){
answerInCityRecords.add(answerRecord);
}
public void addAnswerInProvinceRecords(CallRecord answerRecord){
answerInProvinceRecords.add(answerRecord);
}
public void addAnswerInLandRecords(CallRecord answerRecord){
answerInLandRecords.add(answerRecord);
}
public void addSendMessageRecords(MessageRecord sendMessageRecord){
sendMessageRecords.add(sendMessageRecord);
}
public ArrayList<MessageRecord> getSendMessageRecords() {
return sendMessageRecords;
}
public ArrayList<MessageRecord> getReceiveMessageRecords() {
return receiveMessageRecords;
}
public ArrayList<CallRecord> getCallingInCityRecords() {
return callingInCityRecords;
}
public ArrayList<CallRecord> getCallingInProvinceRecords() {
return callingInProvinceRecords;
}
public ArrayList<CallRecord> getCallingInLandRecords() {
return callingInLandRecords;
}
public ArrayList<CallRecord> getAnswerInCityRecords() {
return answerInCityRecords;
}
public ArrayList<CallRecord> getAnswerInProvinceRecords() {
return answerInProvinceRecords;
}
public ArrayList<CallRecord> getAnswerInLandRecords() {
return answerInLandRecords;
}
}
abstract class CommunicationRecord{
protected String callingNumber;
protected String answerNumber;
public String getCallingNumber() {
return callingNumber;
}
public void setCallingNumber(String callingNumber) {
this.callingNumber = callingNumber;
}
public String getAnswerNumber() {
return answerNumber;
}
public void setAnswerNumber(String answerNumber) {
this.answerNumber = answerNumber;
}
}
class CallRecord extends CommunicationRecord{
private Date startTime;
private Date endTime;
private String callingAddressAreaCode;
private String answerAddressAreaCode;
CallRecord(String[] inputs) {
super();
if (inputs[0].length() == 10) {
callingAddressAreaCode = inputs[0].substring(0, 3);
} else {
callingAddressAreaCode = inputs[0].substring(0, 4);
}
if (inputs[1].length() == 10) {
answerAddressAreaCode = inputs[1].substring(0, 3);
} else {
answerAddressAreaCode = inputs[1].substring(0, 4);
}
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy.MM.dd HH:mm:ss", Locale.getDefault());
try {
startTime = simpleDateFormat.parse(inputs[2] + " " + inputs[3]);
endTime = simpleDateFormat.parse(inputs[4] + " " + inputs[5]);
} catch (ParseException e) {
}
}
CallRecord(Date startTime, Date endTime, String callingAddressAreaCode, String answerAddressAreaCode) {
super();
this.startTime = startTime;
this.endTime = endTime;
this.callingAddressAreaCode = callingAddressAreaCode;
this.answerAddressAreaCode = answerAddressAreaCode;
}
public int getCallType() {
if (callingAddressAreaCode.equals(answerAddressAreaCode)) {
return 1;
}
if (callingAddressAreaCode.matches("^079[0-9]$") || callingAddressAreaCode.equals("0701")) {
if (answerAddressAreaCode.matches("^079[0-9]$") || answerAddressAreaCode.equals("0701")) {
return 2;
}
}
return 3;
}
public Date getStartTime(){
return this.startTime;
}
public void setStartTime(Date startTime){
this.startTime=startTime;
}
public Date getEndTime(){
return this.endTime;
}
public void setEndTime(Date endTime){
this.endTime=endTime;
}
public String getCallingAddressAreaCode(){
return this.callingAddressAreaCode;
}
public void setCallingAddressAreaCode(String callingAddressAreaCode){
this.callingAddressAreaCode=callingAddressAreaCode;
}
public String getAnswerAddressAreaCode(){
return this.answerAddressAreaCode;
}
public void setAnswerAddressAreaCode(String answerAddressAreaCode){
this.answerAddressAreaCode=answerAddressAreaCode;
}
}
class MessageRecord extends CommunicationRecord{
private String message;
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
}
abstract class ChargeRule{
public abstract double calCost(UserRecords userRecords);
}
abstract class CallChargeRule extends ChargeRule{
public abstract double calCost(UserRecords userRecords);
}
class LandPhoneInCityRule extends CallChargeRule{
@Override
public double calCost(UserRecords userRecords){
double SumCost = 0;
for(CallRecord call:userRecords.getCallingInCityRecords()){
double distanceS=(call.getEndTime().getTime()-call.getStartTime().getTime())/1000;//getTime获取毫秒
if(distanceS>0){
double distanceM=(int)distanceS/60;
if(distanceS%60!=0)
distanceM++;
SumCost+=distanceM*0.1;
}
}
return SumCost;
}
}
class LandPhoneInProvinceRule extends CallChargeRule{
@Override
public double calCost(UserRecords userRecords){
double SumCost = 0;
for(CallRecord call:userRecords.getCallingInProvinceRecords()){
double distanceS=(call.getEndTime().getTime()-call.getStartTime().getTime())/1000;//getTime获取毫秒
if(distanceS>0){
double distanceM=(int)distanceS/60;
if(distanceS%60!=0)
distanceM++;
SumCost+=distanceM*0.3;
}
}
return SumCost;
}
}
class LandPhoneInlandRule extends CallChargeRule{
@Override
public double calCost(UserRecords userRecords){
double SumCost = 0;
for(CallRecord call:userRecords.getCallingInLandRecords()){
double distanceS=(call.getEndTime().getTime()-call.getStartTime().getTime())/1000;//getTime获取毫秒
if(distanceS>0){
double distanceM=(int)distanceS/60;
if(distanceS%60!=0)
distanceM++;
SumCost+=distanceM*0.6;
}
}
return SumCost;
}
}
class Common {
public static double round(double m,int n) {
return (double)Math.round(m*Math.pow(10,n))/Math.pow(10,n);
}
}
idea生成类图:
SourceMonitor的生成报表内容:
因嵌套了太多的if/else导致代码几乎大部分都不合格,平均复杂度是15,远远超过绿色范围(所推荐的java良好代码,如平均复杂度在2.0-4.0之间)
第7次大作业
SourceMonitor的生成报表内容:
分析:判断字符串的合法性时用到了正则表达式。代码各类指标都良好,平均复杂度为3。
3-7-2 点线形系列2-线的计算
源码:
SourceMonitor的生成报表内容:
分析:代码主架构是switch里面有太多的if/else判断,导致部分指标不合格。
3-7-3 点线形系列3-三角形的计算
SourceMonitor的生成报表内容:
分析:主要问题和上面类似,不过平均复杂度更小。
采坑心得
1-7-2 长度质量计量单位换算
需要将doule类型转化成float类型后输出,如:
System.out.print((float)zl +" "+ (float)cd);
1-7-4 房产税费计算2022
输出时需要将结果转化为float类型,切忌不要出现(flaot)c*100.0
,先将结果括起来再进行类型转换,或者先转化为double类型:
double f=b*5.0;//印花税
double g=d*3.0;//交易费
double h=d*1.36;//测绘费
System.out.print(" "+(float)f+" "+(float)g+" "+(float)h);
1-7-6 学号识别
建议用字符串输入,不要用int,要不然会变得不幸,别问我为什么知道。
1-7-9 二进制数值提取
输入区分使用next和nextLine,个人觉得nextLine会更常用,因为要判别空格等非法字符。
1-7-7 判断三角形类型
...PTA排序出错了。注意精度问题,判断是否为直角三角形:
Math.abs(a*a+b*b-c*c)<0.1//勾股定理
2-7-2 串口字符解析
注意输入的是字符串,进行单个字符比较时加上单引号或者减去某个数值,如下:
if((n+1)%2==s[j]-48&&s[j+1]!='1')//判断奇校验位和结束位
3-7-3 点线形系列3-三角形的计算
保留6位小数,且多余部分采用四舍五入规则进到最低位,参考如下:
S1=(double)Math.round(S1*Math.pow(10,6))/Math.pow(10,6);
涉及到0值判断的都应该考虑精度问题:
if(S1+S2+S3-S<1e-12)//面积相等
改进建议
可以将一些复用性较强的代码封装进自己的编辑器中,比如第三次大作业的点类,线类,三角形类都分别对应了三道小题,前两次作业基本上是简单的输入和输出,复用性不高,可以略过。
总结
1.前两次作业是练手的,目的是熟悉java基本语法;但是第三次作业工作量会剧增,而且类与类之间的联系以及类方法的复用性会增强,如果不对复用性强的代码封装成类,那么Main里面的代码将会变得特别混乱且长,你可能在第一个小问里写过同样的代码,下一问又要用上,没写类只能复制粘贴,而且观感特别不好,因为隔一段时间再去看根本不知道这部分代码实现了啥功能,我对此深有体会,因为我这三次作业都是一股脑全写在Main里面,提交通过后再把全部代码全看懂了再进行封装,花费时间太长了。
2.java时面向对象,而C是面向过程的,需要从C的编码习惯慢慢变为java的编码习惯,这个转化过程是漫长且煎熬的。