PTA题目集4~6总结性Blog

(1)前言

1.题目集4涉及的知识点主要包括程序设计与实现、字符串处理、条件判断等。需要设计一个函数来处理输入信息,包括题目信息、试卷信息、学生信息、答卷信息以及删除题目信息。这个函数需要能够识别不同类型的输入信息,并将其按照题目、试卷、学生等分类存储起来。还需要设计合适的数据结构来存储题目信息、试卷信息、学生信息以及答卷信息。难度较高,因为要考虑各种题型(选择题、填空题)、多张试卷、删除题目、学生答卷、判题逻辑等复杂情况。
2.题目集5涉及的知识点主要包括输入输出处理:对输入信息进行解析和处理,根据控制信息输出设备的状态或参数,要求能熟练运用正则表达式,同时更需要理解家居电路中各种设备的工作原理,包括开关、调速器、灯具和风扇等,以及它们之间的连接关系。需要使用正则表达式处理控制命令,根据不同命令执行相应的操作。难度属于中等偏上,因为还要处理设备连接命令,根据连接情况更新设备的电压状态。
3.题目集6涉及的知识点跟题目集5差不多,也要对输入输出进行处理,要能熟练运用正则表达式,其他知识点还包括设备控制逻辑:理解开关、调速器和受控设备的控制逻辑,包括状态切换、档位调节等,强电电路设计:涉及电路中的电压、电流等物理量的计算和处理。难度上来说,这道题的难度较大,需要综合考虑物理电路知识、逻辑控制和数据处理等方面的内容,比起题目集5的7-1,迭代成了并联电路,需要考虑分压等,更加需要巧妙的设计。

(2)设计与分析

题目集4 7-1

7-1 答题判题程序-4
分数 82
困难
作者 蔡轲
单位 南昌航空大学
设计实现答题程序,模拟一个小型的测试,要求输入题目信息、试卷信息、答题信息、学生信息、删除题目信息,根据输入题目信息中的标准答案判断答题的结果。本题在答题判题程序-3基础上新增的内容统一附加在输出格式说明之后,用粗体标明。

输入格式:

程序输入信息分五种,信息可能会打乱顺序混合输入。

1、题目信息
题目信息为独行输入,一行为一道题,多道题可分多行输入。

格式:"#N:"+题目编号+" "+"#Q:"+题目内容+" "#A:"+标准答案
格式约束:
1、题目的输入顺序与题号不相关,不一定按题号顺序从小到大输入。
2、允许题目编号有缺失,例如:所有输入的题号为1、2、5,缺少其中的3号题。此种情况视为正常。
样例:#N:1 #Q:1+1= #A:2
#N:2 #Q:2+2= #A:4

2、试卷信息

试卷信息为独行输入,一行为一张试卷,多张卷可分多行输入数据。 \

格式:"#T:"+试卷号+" "+题目编号+"-"+题目分值+" "+题目编号+"-"+题目分值+...
格式约束:
题目编号应与题目信息中的编号对应。
一行信息中可有多项题目编号与分值。
样例:#T:1 3-5 4-8 5-2

3、学生信息

学生信息只输入一行,一行中包括所有学生的信息,每个学生的信息包括学号和姓名,格式如下。

格式:"#X:"+学号+" "+姓名+"-"+学号+" "+姓名....+"-"+学号+" "+姓名
格式约束:
答案数量可以不等于试卷信息中题目的数量,没有答案的题目计0分,多余的答案直接忽略,答案之间以英文空格分隔。
样例:
#S:1 #A:5 #A:22
1是试卷号
5是1号试卷的顺序第1题的题目答案
4、答卷信息

答卷信息按行输入,每一行为一张答卷的答案,每组答案包含某个试卷信息中的题目的解题答案,答案的顺序号与试 卷信息中的题目顺序相对应。答卷中:

格式:"#S:"+试卷号+" "+学号+" "+"#A:"+试卷题目的顺序号+"-"+答案内容+...
格式约束:
答案数量可以不等于试卷信息中题目的数量,没有答案的题目计0分,多余的答案直接忽略,答案之间以英文空格分隔。
答案内容可以为空,即””。
答案内容中如果首尾有多余的空格,应去除后再进行判断。
答卷信息中仅包含试卷号、学号,而没有后续内容的,视为一张空白卷,为有效信息,不做格式错误处理。
样例:
#T:1 1-5 3-2 2-5 6-9 4-10 7-3
#S:1 20201103 #A:2-5 #A:6-4
1是试卷号
20201103是学号
2-5中的2是试卷中顺序号,5是试卷第2题的答案,即T中3-2的答案
6-4中的6是试卷中顺序号,4是试卷第6题的答案,即T中7-3的答案
注意:不要混淆顺序号与题号

5、删除题目信息

删除题目信息为独行输入,每一行为一条删除信息,多条删除信息可分多行输入。该信息用于删除一道题目信息,题目被删除之后,引用该题目的试卷依然有效,但被删除的题目将以0分计,同时在输出答案时,题目内容与答案改为一条失效提示,例如:”the question 2 invalid~0”

格式:"#D:N-"+题目号
格式约束:
题目号与第一项”题目信息”中的题号相对应,不是试卷中的题目顺序号。
本题暂不考虑删除的题号不存在的情况。
样例:
//#N:1 #Q:1+1= #A:2
//#N:2 #Q:2+2= #A:4
//#T:1 1-5 2-8
//#X:20201103 Tom-20201104 Jack
//#S:1 20201103 #A:1-5 #A:2-4
//#D:N-2
//end

输出:
alert: full score of test paper1 is not 100 points
1+1=5false
the question 2 invalid~0
20201103 Tom: 0 0~0
答题信息以一行"end"标记结束,"end"之后的信息忽略。

输出格式:

1、试卷总分警示

该部分仅当一张试卷的总分分值不等于100分时作提示之用,试卷依然属于正常试卷,可用于后面的答题。如果总分等于100 分,该部分忽略,不输出。
格式:"alert: full score of test paper"+试卷号+" is not 100 points"
约束:有多张试卷时,按输入信息的先后顺序输出警示。

样例:alert: full score of test paper2 is not 100 points

2、答卷信息

一行为一道题的答题信息,根据试卷的题目的数量输出多行数据。

格式:题目内容+""+答案++""+判题结果(true/false)

约束:如果输入的答案信息少于试卷的题目数量,每一个缺失答案的题目都要输出"answer is null" 。

样例:

 answer is null

 3+2=~5~true

 4+6=~22~false.

 answer is null

3、判分信息

判分信息为一行数据,是一条答题记录所对应试卷的每道小题的计分以及总分,计分输出的先后顺序与题目题号相对应。

格式:学号+" "+姓名+": "+题目得分+" "+....+题目得分+"~"+总分
格式约束:
1、没有输入答案的题目、被删除的题目、答案错误的题目计0分
2、判题信息的顺序与输入答题信息中的顺序相同
样例:20201103 Tom: 0 0~0
根据输入的答卷的数量以上2、3项答卷信息与判分信息将重复输出。

4、被删除的题目提示信息

当某题目被试卷引用,同时被删除时,答案中输出提示信息。样例见第5种输入信息“删除题目信息”。

5、题目引用错误提示信息

试卷错误地引用了一道不存在题号的试题,在输出学生答案时,提示”non-existent question~”加答案。例如:

输入:
//#N:1 #Q:1+1= #A:2
//#T:1 3-8
//#X:20201103 Tom-20201104 Jack-20201105 Www
//#S:1 20201103 #A:1-4
//end
输出:
alert: full score of test paper1 is not 100 points
non-existent question~0
20201103 Tom: 0~0
如果答案输出时,一道题目同时出现答案不存在、引用错误题号、题目被删除,只提示一种信息,答案不存在的优先级最高,例如:

输入:
//#N:1 #Q:1+1= #A:2
//#T:1 3-8
//#X:20201103 Tom-20201104 Jack-20201105 Www
//#S:1 20201103
//end
输出:
alert: full score of test paper1 is not 100 points
answer is null
20201103 Tom: 0~0
6、格式错误提示信息

输入信息只要不符合格式要求,均输出”wrong format:”+信息内容。

  例如:wrong format:2 #Q:2+2= #4

7、试卷号引用错误提示输出

如果答卷信息中试卷的编号找不到,则输出”the test paper number does not exist”,答卷中的答案不用输出,参见样例8。

8、学号引用错误提示信息

如果答卷中的学号信息不在学生列表中,答案照常输出,判分时提示错误。参见样例9。

本次作业新增内容:

1、输入选择题题目信息

题目信息为独行输入,一行为一道题,多道题可分多行输入。

格式:"#Z:"+题目编号+" "+"#Q:"+题目内容+" "#A:"+标准答案
格式基本的约束与一般的题目输入信息一致。

新增约束:标准答案中如果包含多个正确答案(多选题),正确答案之间用英文空格分隔。
例如:
//#Z:2 #Q:宋代书法有苏黄米蔡四家,分别是: #A:苏轼 黄庭坚 米芾 蔡襄
多选题输出:

输出格式与一般答卷题目的输出一致,判断结果除了true、false,增加一项”partially correct”表示部分正确。

多选题给分方式:

答案包含所有正确答案且不含错误答案给满分;包含一个错误答案或完全没有答案给0分;包含部分正确答案且不含错误答案给一半分,如果一半分值为小数,按截尾规则只保留整数部分。
例如:
//#N:1 #Q:1+1= #A:2
//#Z:2 #Q:党十八大报告提出要加强()建设。A 政务诚信 B 商务诚信 C社会诚信 D司法公信 #A:A B C D
//#T:1 1-5 2-9
//#X:20201103 Tom
//#S:1 20201103 #A:1-5 #A:2-A C
//end
输出:
alert: full score of test paper1 is not 100 points
1+1=5false
党十八大报告提出要加强()建设。A 政务诚信 B 商务诚信 C社会诚信 D司法公信~A C~partially correct
20201103 Tom: 0 4~4

2、输入填空题题目信息

题目信息为独行输入,一行为一道题,多道题可分多行输入。

格式:"#K:"+题目编号+" "+"#Q:"+题目内容+" "#A:"+标准答案
格式基本的约束与一般的题目输入信息一致。
例如:#K:2 #Q:古琴在古代被称为: #A:瑶琴或七弦琴
填空题输出:

输出格式与一般答卷题目的输出一致,判断结果除了true、false,增加一项”partially correct”表示部分正确。

填空题给分方式:

答案与标准答案内容完全匹配给满分,包含一个错误字符或完全没有答案给0分,包含部分正确答案且不含错误字符给一半分,如果一半分值为小数,按截尾规则只保留整数部分。

例如:
//#N:1 #Q:1+1= #A:2
//#K:2 #Q:古琴在古代被称为: #A:瑶琴或七弦琴
//#T:1 1-5 2-10
//#X:20201103 Tom
//#S:1 20201103 #A:1-5 #A:2-瑶琴
//end
输出:
alert: full score of test paper1 is not 100 points
1+1=5false
古琴在古代被称为:瑶琴partially correct
20201103 Tom: 0 5~5

3、输出顺序变化

只要是正确格式的信息,可以以任意的先后顺序输入各类不同的信息。比如试卷可以出现在题目之前,删除题目的信息可以出现在题目之前等。

例如:
//#T:1 1-5 2-10
//#N:1 #Q:1+1= #A:2
//#K:2 #Q:古琴在古代被称为: #A:瑶琴或七弦琴
//#X:20201103 Tom
//#S:1 20201103 #A:1-5 #A:2-古筝
//end
输出:
alert: full score of test paper1 is not 100 points
1+1=5false
古琴在古代被称为:古筝false
20201103 Tom: 0 0~0

4、多张试卷信息

本题考虑多个同学有多张不同试卷的答卷的情况。输出顺序优先级为学号、试卷号,按从小到大的顺序先按学号排序,再按试卷号。

例如:
//#T:1 1-5 2-10
//#T:2 1-8 2-21
//#N:1 #Q:1+1= #A:2
//#S:2 20201103 #A:1-2 #A:2-古筝
//#S:1 20201103 #A:1-5 #A:2-瑶琴或七弦琴
//#S:1 20201104 #A:1-2 #A:2-瑟
//#S:2 20201104 #A:1-5 #A:2-七弦琴
//#X:20201103 Tom-20201104 Jack
//#K:2 #Q:古琴在古代被称为: #A:瑶琴或七弦琴
//end
输出:
alert: full score of test paper1 is not 100 points
alert: full score of test paper2 is not 100 points
1+1=5false
古琴在古代被称为:瑶琴或七弦琴true
20201103 Tom: 0 10~10
1+1=2true
古琴在古代被称为:古筝false
20201103 Tom: 8 0~8
1+1=2true
古琴在古代被称为:false
20201104 Jack: 5 0~5
1+1=5false
古琴在古代被称为:七弦琴partially correct
20201104 Jack: 0 10~10
新增的题目异常情况的处理与一般题目相同,具体样例参考上一次大作业的样例说明:
答题判题程序-3题面.pdf

输入样例1:
多选题测试,不含删除。例如:

//#N:1 #Q:1+1= #A:2
//#Z:2 #Q:党十八大报告提出要加强()建设。A 政务诚信 B 商务诚信 C社会诚信 D司法公信 #A:A B C D
//#T:1 1-5 2-9
//#X:20201103 Tom
//#S:1 20201103 #A:1-5 #A:2-A C
//end
输出样例1:
在这里给出相应的输出。例如:

alert: full score of test paper1 is not 100 points
1+1=5false
党十八大报告提出要加强()建设。A 政务诚信 B 商务诚信 C社会诚信 D司法公信~A C~partially correct
20201103 Tom: 0 4~4
输入样例2:
填空题测试,不含删除。例如:

//#N:1 #Q:1+1= #A:2
//#K:2 #Q:古琴在古代被称为: #A:瑶琴或七弦琴
//#T:1 1-5 2-10
//#X:20201103 Tom
//#S:1 20201103 #A:1-5 #A:2-瑶琴
//end
输出样例2:
在这里给出相应的输出。例如:

alert: full score of test paper1 is not 100 points
1+1=5false
古琴在古代被称为:瑶琴partially correct
20201103 Tom: 0 5~5
输入样例3:
乱序测试,不含删除。例如:

//#T:1 1-5 2-10
//#N:1 #Q:1+1= #A:2
//#K:2 #Q:古琴在古代被称为: #A:瑶琴或七弦琴
//#X:20201103 Tom
//#S:1 20201103 #A:1-5 #A:2-古筝
//end
输出样例3:
在这里给出相应的输出。例如:

alert: full score of test paper1 is not 100 points
1+1=5false
古琴在古代被称为:古筝false
20201103 Tom: 0 0~0
输入样例4:
两个同学多张不同试卷的答卷,不含删除。例如:

//#T:1 1-5 2-10
//#T:2 1-8 2-21
//#N:1 #Q:1+1= #A:2
//#S:2 20201103 #A:1-2 #A:2-古筝
//#S:1 20201104 #A:1-2 #A:2-瑟
//#S:1 20201103 #A:1-5 #A:2-瑶琴或七弦琴
//#S:2 20201104 #A:1-5 #A:2-七弦琴
//#X:20201103 Tom-20201104 Jack
//#K:2 #Q:古琴在古代被称为: #A:瑶琴或七弦琴
//end
输出样例4:
在这里给出相应的输出。例如:

alert: full score of test paper1 is not 100 points
alert: full score of test paper2 is not 100 points
1+1=5false
古琴在古代被称为:瑶琴或七弦琴true
20201103 Tom: 0 10~10
1+1=2true
古琴在古代被称为:古筝false
20201103 Tom: 8 0~8
1+1=2true
古琴在古代被称为:false
20201104 Jack: 5 0~5
1+1=5false
古琴在古代被称为:七弦琴partially correct
20201104 Jack: 0 10~10
代码长度限制
50 KB
时间限制
1000 ms
内存限制
64 MB
栈限制
8192 KB

代码如下:

import java.util.*;

class Question {
    String id;
    String content;
    String answer;
    String type;

    Question(String id, String content, String answer, String type) {
        this.id = id;
        this.content = content;
        this.answer = answer;
        this.type = type;
    }
}

class Paper {
    String id;
    Map<String, Integer> questions; // question id -> score

    Paper(String id) {
        this.id = id;
        this.questions = new LinkedHashMap<>();
    }

    void addQuestion(String questionId, int score) {
        this.questions.put(questionId, score);
    }

    int totalScore() {
        int total = 0;
        for (int score : questions.values()) {
            total += score;
        }
        return total;
    }
}

class Student {
    String id;
    String name;

    Student(String id, String name) {
        this.id = id;
        this.name = name;
    }
}

class Answer {
    String paperId;
    String studentId;
    Map<Integer, String> answers; // question index in the paper -> answer

    Answer(String paperId, String studentId) {
        this.paperId = paperId;
        this.studentId = studentId;
        this.answers = new HashMap<>();
    }

    void addAnswer(int questionIndex, String answer) {
        this.answers.put(questionIndex, answer);
    }
}

public class Main {
    static Map<String, Question> questions = new HashMap<>();
    static Map<String, Paper> papers = new HashMap<>();
    static Map<String, Student> students = new HashMap<>();
    static List<Answer> answers = new ArrayList<>();
    static Set<String> deletedQuestions = new HashSet<>();
    
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        List<String> inputs = new ArrayList<>();
        while (sc.hasNextLine()) {
            String line = sc.nextLine().trim();
            if (line.equals("end")) break;
            inputs.add(line);
        }
        
        for (String input : inputs) {
            if (input.startsWith("#N:") || input.startsWith("#Z:") || input.startsWith("#K:")) {
                addQuestion(input);
            } else if (input.startsWith("#T:")) {
                addPaper(input);
            } else if (input.startsWith("#X:")) {
                addStudents(input);
            } else if (input.startsWith("#S:")) {
                addAnswer(input);
            } else if (input.startsWith("#D:")) {
                deleteQuestion(input);
            } else {
                System.out.println("wrong format:" + input);
            }
        }
        
        processAnswers();
    }

    private static void addQuestion(String input) {
        try {
            String[] parts = input.split(" ");
            String id = parts[0].substring(3);
            String type = parts[0].substring(0, 2);
            String content = parts[1].substring(3);
            String answer = parts[2].substring(3);
            questions.put(id, new Question(id, content, answer, type));
        } catch (Exception e) {
            System.out.println("wrong format:" + input);
        }
    }

    private static void addPaper(String input) {
        try {
            String[] parts = input.split(" ");
            String id = parts[0].substring(3);
            Paper paper = new Paper(id);
            for (int i = 1; i < parts.length; i++) {
                String[] q = parts[i].split("-");
                paper.addQuestion(q[0], Integer.parseInt(q[1]));
            }
            papers.put(id, paper);
        } catch (Exception e) {
            System.out.println("wrong format:" + input);
        }
    }

    private static void addStudents(String input) {
        try {
            String[] parts = input.substring(3).split("-");
            for (String part : parts) {
                String[] student = part.split(" ");
                students.put(student[0], new Student(student[0], student[1]));
            }
        } catch (Exception e) {
            System.out.println("wrong format:" + input);
        }
    }

    private static void addAnswer(String input) {
        try {
            String[] parts = input.split(" ");
            String paperId = parts[0].substring(3);
            String studentId = parts[1];
            Answer answer = new Answer(paperId, studentId);
            for (int i = 2; i < parts.length; i++) {
                String[] ans = parts[i].substring(3).split("-");
                answer.addAnswer(Integer.parseInt(ans[0]), ans[1]);
            }
            answers.add(answer);
        } catch (Exception e) {
            System.out.println("wrong format:" + input);
        }
    }

    private static void deleteQuestion(String input) {
        try {
            String questionId = input.substring(4);
            deletedQuestions.add(questionId);
        } catch (Exception e) {
            System.out.println("wrong format:" + input);
        }
    }

    private static void processAnswers() {
        for (Paper paper : papers.values()) {
            if (paper.totalScore() != 100) {
                System.out.println("alert: full score of test paper" + paper.id + " is not 100 points");
            }
        }

        for (Answer answer : answers) {
            Paper paper = papers.get(answer.paperId);
            if (paper == null) {
                System.out.println("the test paper number does not exist");
                continue;
            }

            Student student = students.get(answer.studentId);
            if (student == null) {
                System.out.println(answer.studentId + ": student does not exist");
                continue;
            }

            int totalScore = 0;
            StringBuilder result = new StringBuilder();
            result.append(answer.studentId).append(" ").append(student.name).append(": ");

            int i = 1;
            for (String questionId : paper.questions.keySet()) {
                int score = 0;
                String judgeResult = "false";
                Question question = questions.get(questionId);
                String studentAnswer = answer.answers.get(i);

                if (deletedQuestions.contains(questionId)) {
                    System.out.println("the question " + questionId + " invalid~0");
                    result.append("0 ");
                } else if (question == null) {
                    System.out.println("non-existent question~0");
                    result.append("0 ");
                } else if (studentAnswer == null) {
                    System.out.println("answer is null");
                    result.append("0 ");
                } else {
                    studentAnswer = studentAnswer.trim();
                    if (question.type.equals("#N")) {
                        judgeResult = question.answer.equals(studentAnswer) ? "true" : "false";
                        score = judgeResult.equals("true") ? paper.questions.get(questionId) : 0;
                    } else if (question.type.equals("#Z")) {
                        Set<String> correctAnswers = new HashSet<>(Arrays.asList(question.answer.split(" ")));
                        Set<String> studentAnswers = new HashSet<>(Arrays.asList(studentAnswer.split(" ")));
                        if (studentAnswers.equals(correctAnswers)) {
                            judgeResult = "true";
                            score = paper.questions.get(questionId);
                        } else if (studentAnswers.containsAll(correctAnswers)) {
                            judgeResult = "partially correct";
                            score = paper.questions.get(questionId) / 2;
                        } else {
                            judgeResult = "false";
                            score = 0;
                        }
                    } else if (question.type.equals("#K")) {
                        if (question.answer.equals(studentAnswer)) {
                            judgeResult = "true";
                            score = paper.questions.get(questionId);
                        } else if (question.answer.contains(studentAnswer)) {
                            judgeResult = "partially correct";
                            score = paper.questions.get(questionId) / 2;
                        } else {
                            judgeResult = "false";
                            score = 0;
                        }
                    }
                    System.out.println(question.content + "~" + studentAnswer + "~" + judgeResult);
                    result.append(score).append(" ");
                }
                totalScore += score;
                i++;
            }
            result.append("~").append(totalScore);
            System.out.println(result.toString());
        }
    }
}

SourceMonitor分析结果如下:
image
这串代码的设计思路可以总结为以下几个步骤:

  1. 定义数据模型: 首先确定需要处理的数据类型,包括题目(Question)、试卷(Paper)、学生(Student)和答案(Answer)。为每种数据类型定义相应的类,并确定类的属性和方法。

  2. 输入处理: 设计一个主程序,通过读取标准输入流(System.in)来接收用户输入的信息。按照预定的输入格式逐行读取输入,并根据不同的输入类型分别进行处理。

  3. 数据存储: 使用合适的数据结构来存储题目、试卷、学生和答案等数据。在这段代码中,使用了 Map 和 List 来存储数据,其中 Map 用于存储题目、试卷和学生信息,List 用于存储答案信息。

  4. 数据解析: 对每行输入进行解析,提取出关键信息,并根据信息类型进行相应的处理。例如,对于题目信息,解析出题目内容、答案和类型,并创建相应的 Question 对象;对于试卷信息,解析出试卷编号和题目及分数,并创建相应的 Paper 对象。

  5. 数据关联: 将题目、试卷和学生等信息关联起来,以便后续处理。在这段代码中,使用了 Map 来建立试卷和学生的关联关系,并使用 List 来存储答案信息。

  6. 答案评分: 对学生的答案进行评分,根据试卷中的题目类型和标准答案进行比对,并计算得分。在评分过程中,需要考虑不同类型题目的评分标准,例如单选题、多选题、填空题等。

  7. 结果输出: 将评分结果输出到标准输出流(System.out),以供用户查看。在输出时,可以按照规定的格式输出学生的得分情况,包括学生编号、姓名、每题得分和总分。

  8. 异常处理: 对可能出现的异常情况进行处理,例如输入格式错误、试卷题目不完整等。在发生异常时,给出相应的错误提示,并采取适当的措施处理异常,例如终止程序执行或跳过当前输入。

通过以上设计思路,可以实现一个能够有效处理试卷、学生答案和评分的程序,从而满足用户的需求。

题目集5 7-1

7-1 家居强电电路模拟程序-1
分数 75
作者 蔡轲
单位 南昌航空大学
智能家居是在当下家庭中越来越流行的一种配置方案,它通过物联网技术将家中的各种设备(如音视频设备、照明系统、窗帘控制、空调控制、安防系统、数字影院系统、影音服务器、影柜系统、网络家电等)连接到一起,提供家电控制、照明控制、电话远程控制、室内外遥控、防盗报警、环境监测、暖通控制、红外转发以及可编程定时控制等多种功能和手段。与普通家居相比,智能家居不仅具有传统的居住功能,兼备建筑、网络通信、信息家电、设备自动化,提供全方位的信息交互功能。请根据如下要去设计一个智能家居强电电路模拟系统。

1、控制设备模拟

本题模拟的控制设备包括:开关、分档调速器、连续调速器。

开关:包括0和1两种状态。

开关有两个引脚,任意一个引脚都可以是输入引脚,而另一个则是输出引脚。开关状态为0时,无论输入电位是多少,输出引脚电位为0。当开关状态为1时,输出引脚电位等于输入电位。
分档调速器

按档位调整,常见的有3档、4档、5档调速器,档位值从0档-2(3/4)档变化。本次迭代模拟4档调速器,每个档位的输出电位分别为0、0.3、0.6、0.9倍的输入电压。
连续调速器

没有固定档位,按位置比例得到档位参数,数值范围在[0.00-1.00]之间,含两位小数。输出电位为档位参数乘以输入电压。
所有调速器都有两个引脚,一个固定的输入(引脚编号为1)、一个输出引脚(引脚编号为2)。当输入电位为0时,输出引脚输出的电位固定为0,不受各类开关调节的影响。

所有控制设备的初始状态/档位为0。

控制设备的输入引脚编号为1,输出引脚编号为2。

2、受控设备模拟

本题模拟的受控设备包括:灯、风扇。两种设备都有两根引脚,通过两根引脚电压的电压差驱动设备工作。

灯有两种工作状态:亮、灭。在亮的状态下,有的灯会因引脚电位差的不同亮度会有区别。
风扇在接电后有两种工作状态:停止、转动。风扇的转速会因引脚的电位差的不同而有区别。
本次迭代模拟两种灯具。

白炽灯:

亮度在0~200lux(流明)之间。
电位差为0-9V时亮度为0,其他电位差按比例,电位差10V对应50ux,220V对应200lux,其他电位差与对应亮度值成正比。白炽灯超过220V。
日光灯:

亮度为180lux。
只有两种状态,电位差为0时,亮度为0,电位差不为0,亮度为180。
本次迭代模拟一种吊扇。

工作电压区间为80V-150V,对应转速区间为80-360转/分钟。80V对应转速为80转/分钟,150V对应转速为360转/分钟,超过150V转速为360转/分钟(本次迭代暂不考虑电压超标的异常情况)。其他电压值与转速成正比,输入输出电位差小于80V时转速为0。
输入信息:

1、设备信息

分别用设备标识符K、F、L、B、R、D分别表示开关、分档调速器、连续调速器、白炽灯、日光灯、吊扇。

设备标识用标识符+编号表示,如K1、F3、L2等。
引脚格式:设备标识-引脚编号,例如:K1-1标识编号为1的开关的输入引脚。

三种控制开关的输入引脚编号为1,输出引脚编号为2。
受控设备的两个引脚编号分别为1、2。
约束条件:

不同设备的编号可以相同。
同种设备的编号可以不连续。
设备信息不单独输入,包含在连接信息中。

2、连接信息

一条连接信息占一行,用[]表示一组连接在一起的设备引脚,引脚与引脚之间用英文空格" "分隔。

格式:"["+引脚号+" "+...+" "+引脚号+"]"
例如:[K1-1 K3-2 D5-1]表示K1的输入引脚,K3的输出引脚,D5的1号引脚连接在一起。
约束条件:

本次迭代不考虑两个输出引脚短接的情况
考虑调速器输出串联到其他控制设备(开关)的情况
不考虑调速器串联到其他调速器的情况。
不考虑各类控制设备的并联接入或反馈接入。例如,K1的输出接到L2的输入,L2的输出再接其他设备属于串联接线。K1的输出接到L2的输出,同时K1的输入接到L2的输入,这种情况属于并联。K1的输出接到L2的输入,K1的输入接到L2的输出,属于反馈接线。
3、控制设备调节信息

开关调节信息格式:

//#+设备标识K+设备编号,例如:#K2,代表切换K2开关的状态。
分档调速器的调节信息格式:

//#+设备标识F+设备编号+"+" 代表加一档,例如:#F3+,代表F3输出加一档。
//#+设备标识F+设备编号+"-" 代表减一档,例如:#F1-,代表F1输出减一档。
连续调速器的调节信息格式:

//#+设备标识L+设备编号+":" +数值 代表将连续调速器的档位设置到对应数值,例如:#L3:0.6,代表L3输出档位参数0.6。
4、电源接地标识:VCC,电压220V,GND,电压0V。没有接线的引脚默认接地,电压为0V。

输入信息以end为结束标志,忽略end之后的输入信息。

输出信息:

按开关、分档调速器、连续调速器、白炽灯、日光灯、吊扇的顺序依次输出所有设备的状态或参数。每个设备一行。同类设备按编号顺序从小到大输出。

输出格式:@设备标识+设备编号+":" +设备参数值(控制开关的档位或状态、灯的亮度、风扇的转速,只输出值,不输出单位)
连续调速器的档位信息保留两位小数,即使小数为0,依然显示两位小数.00。
开关状态为0(打开)时显示turned on,状态为1(合上)时显示closed
如:
@K1:turned on
@B1:190
@L1:0.60
本题不考虑输入电压或电压差超过220V的情况。

本题只考虑串联的形式,所以所有测试用例的所有连接信息都只包含两个引脚

本题电路中除了开关可能出现多个,其他电路设备均只出现一次。
电源VCC一定是第一个连接的第一项,接地GND一定是最后一个连接的后一项。

家居电路模拟系列所有题目的默认规则:

1、当计算电压值等数值的过程中,最终结果出现小数时,用截尾规则去掉小数部分,只保留整数部分。为避免精度的误差,所有有可能出现小数的数值用double类型保存并计算,不要作下转型数据类型转换,例如电压、转速、亮度等,只有在最后输出时再把计算结果按截尾规则,舍弃尾数,保留整数输出。

2、所有连接信息按电路从电源到接地的顺序依次输入,不会出现错位的情况。

3、连接信息如果只包含两个引脚,靠电源端的引脚在前,靠接地端的在后。

4、对于调速器,其输入端只会直连VCC,不会接其他设备。整个电路中最多只有一个调速器,且连接在电源上。

家居电路模拟系列1-4题目后续迭代设计:

1、电路结构变化:

迭代1:只有一条线路,所有元件串联
迭代2:线路中包含一个并联电路
迭代3:线路中包含多个串联起来的并联电路
迭代4:并联电路之间可能出现包含关系

电路结构变化示意图见图1。

2、输入信息的变化

串联线路信息:用于记录一段串联电路的元件与连接信息。

例如: #T1:[IN K1-1] [K1-2 D2-1] [D2-2 OUT]
#T1:[IN K1-1] [K1-2 M1-IN][M1-OUT D2-1] [D2-2 GND]
并联线路信息:用于记录一段并联电路所包含的所有串联电路信息。

例如:#M1:[T1 T2 T3]
以上格式仅做参考,格式细节可能会调整,以具体发布的为准。

3、计算方式的变化

迭代1只包含1个受控元件,不用计算电流,之后的电路计算要包含电流、电阻等电路参数。

4、电路元件的变化

每次迭代会增加1-2个新的电路元件。

image

图1:电路结构示意图

设计建议:

1、电路设备类:描述所有电路设备的公共特征。

2、受控设备类、控制设备类:对应受控、控制设备

3、串联电路类:一条由多个电路设备构成的串联电路,也看成是一个独立的电路设备

其他类以及类的属性、方法自行设计。

image

图2:建议设计类图

输入样例1:
在这里给出一组输入。例如:

//[VCC K1-1]
//[K1-2 D2-1]
//[D2-2 GND]
//#K1
//end
输出样例1:
在这里给出相应的输出。例如:

@K1:closed
@D2:360
输入样例2:
在这里给出一组输入。例如:

//[VCC K1-1]
//[K1-2 D2-1]
//[D2-2 GND]
//#K1
//#K1
//end
输出样例2:
在这里给出相应的输出。例如:

@K1:turned on
@D2:0
输入样例3:
在这里给出一组输入。例如:

//[VCC F1-1]
//[F1-2 D2-1]
//[D2-2 GND]
//#F1+
//end
输出样例3:
在这里给出相应的输出。例如:

@F1:1
@D2:0
输入样例4:
在这里给出一组输入。例如:

//[VCC F1-1]
//[F1-2 D2-1]
//[D2-2 GND]
//#F1+
//#F1+
//end
输出样例4:
在这里给出相应的输出。例如:

@F1:2
@D2:288
输入样例5:
在这里给出一组输入。例如:

//[VCC F1-1]
//[F1-2 D2-1]
//[D2-2 GND]
//#F1+
//#F1+
//#F1+
//end
输出样例5:
在这里给出相应的输出。例如:

@F1:3
@D2:360

输入样例6:
在这里给出一组输入。例如:

//[VCC L1-1]
//[L1-2 D2-1]
//[D2-2 GND]
//#L1:1.00
//end
输出样例6:
在这里给出相应的输出。例如:

@L1:1.00
@D2:360
输入样例7:
在这里给出一组输入。例如:

//[VCC L1-1]
//[L1-2 D2-1]
//[D2-2 GND]
//#L1:0.68
//end
输出样例7:
在这里给出相应的输出。例如:

@L1:0.68
@D2:358
输入样例8:
在这里给出一组输入。例如:

//[VCC L1-1]
//[L1-2 B2-1]
//[B2-2 GND]
//#L1:0.68
//end
输出样例8:
在这里给出相应的输出。例如:

@L1:0.68
@B2:149
输入样例9:
在这里给出一组输入。例如:

//[VCC L1-1]
//[L1-2 B2-1]
//[B2-2 GND]
//#L1:1.00
//end
输出样例9:
在这里给出相应的输出。例如:

@L1:1.00
@B2:200
输入样例10:
在这里给出一组输入。例如:

//[VCC L1-1]
//[L1-2 R2-1]
//[R2-2 GND]
//#L1:1.00
//end
输出样例10:
在这里给出相应的输出。例如:

@L1:1.00
@R2:180
代码长度限制
50 KB
时间限制
1000 ms
内存限制
64 MB
栈限制
8192 KB

代码如下:

import java.util.*;
import java.util.regex.*;

abstract class ElectricDevice {
    String name;
    double voltage;

    public ElectricDevice(String name) {
        this.name = name;
        this.voltage = 0;
    }

    public abstract void updateVoltage(double voltage);

    public abstract String getStatus();
}

class Switch extends ElectricDevice {
    private boolean isOn;

    public Switch(String name) {
        super(name);
        this.isOn = false;
    }

    public void toggle() {
        isOn = !isOn;
    }

    @Override
    public void updateVoltage(double voltage) {
        this.voltage = isOn ? voltage : 0;
    }

    @Override
    public String getStatus() {
        return "@" + name + ":" + (isOn ? "closed" : "turned on");
    }
}

class FenSpeeder extends ElectricDevice {
    private int level;
    private static final double[] MULTIPLIERS = {0, 0.3, 0.6, 0.9};

    public FenSpeeder(String name) {
        super(name);
        this.level = 0;
    }

    public void increaseLevel() {
        if (level < MULTIPLIERS.length - 1) {
            level++;
        }
    }

    public void decreaseLevel() {
        if (level > 0) {
            level--;
        }
    }

    @Override
    public void updateVoltage(double voltage) {
        this.voltage = voltage * MULTIPLIERS[level];
    }

    @Override
    public String getStatus() {
        return "@" + name + ":" + level;
    }
}

class LianSpeeder extends ElectricDevice {
    private double multiplier;

    public LianSpeeder(String name) {
        super(name);
        this.multiplier = 0.00;
    }

    public void setMultiplier(double multiplier) {
        this.multiplier = multiplier;
    }

    @Override
    public void updateVoltage(double voltage) {
        this.voltage = voltage * multiplier;
    }

    @Override
    public String getStatus() {
        return "@" + name + ":" + String.format("%.2f", multiplier);
    }
}

class BaiLamp extends ElectricDevice {
    private double brightness;

    public BaiLamp(String name) {
        super(name);
        this.brightness = 0;
    }

    @Override
    public void updateVoltage(double voltage) {
        if (voltage <= 9) {
            brightness = 0;
        } else if (voltage > 220) {
            brightness = 200;
        } else {
            brightness = (voltage - 10) * (200.0 / 210.0);
        }
    }

    @Override
    public String getStatus() {
        return "@" + name + ":" + (int) brightness;
    }
}

class DayLamp extends ElectricDevice {
    private double brightness;

    public DayLamp(String name) {
        super(name);
        this.brightness = 0;
    }

    @Override
    public void updateVoltage(double voltage) {
        brightness = voltage == 0 ? 0 : 180;
    }

    @Override
    public String getStatus() {
        return "@" + name + ":" + (int) brightness;
    }
}

class Fan extends ElectricDevice {
    private double speed;

    public Fan(String name) {
        super(name);
        this.speed = 0;
    }

    @Override
    public void updateVoltage(double voltage) {
        voltage = 220;

        calculateSpeed(voltage);
    }

    private void calculateSpeed(double voltage) {
        if (voltage < 80) {
            speed = 0;
        } else if (voltage >= 150) {
            speed = 360;
        } else {
            speed = (voltage - 80) * (280.0 / 70.0) + 80;
        }
    }

    @Override
    public String getStatus() {
        return "@" + name + ":" +  (int) speed;
    }
}

public class Main {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);

        LinkedHashMap<String, ElectricDevice> deviceMap = new LinkedHashMap<>();

        while (true) {
            String input = scanner.nextLine().trim();
            if (input.equals("end")) {
                break;
            }

            if (input.startsWith("#")) {
                handleControlCommand(input, deviceMap);
            } else if (input.startsWith("[")) {
                handleConnectionCommand(input, deviceMap);
            }
        }

        for (ElectricDevice device : deviceMap.values()) {
            System.out.println(device.getStatus());
        }
    }

    private static void handleControlCommand(String input, LinkedHashMap<String, ElectricDevice> deviceMap) {
        if (input.matches("#K\\d+")) {
            String name = input.substring(1);
            ((Switch) deviceMap.get(name)).toggle();
        } else if (input.matches("#F\\d+\\+")) {
            String name = input.substring(1, input.length() - 1);
            ((FenSpeeder) deviceMap.get(name)).increaseLevel();
        } else if (input.matches("#F\\d+-")) {
            String name = input.substring(1, input.length() - 1);
            ((FenSpeeder) deviceMap.get(name)).decreaseLevel();
        } else if (input.matches("#L\\d+:\\d+\\.\\d{2}")) {
            String[] parts = input.substring(1).split(":");
            String name = parts[0];
            double multiplier = Double.parseDouble(parts[1]);
            ((LianSpeeder) deviceMap.get(name)).setMultiplier(multiplier);
        }
    }

    private static void handleConnectionCommand(String input, LinkedHashMap<String, ElectricDevice> deviceMap) {
        Pattern pattern = Pattern.compile("\\[(.*?)\\]");
        Matcher matcher = pattern.matcher(input);

        while (matcher.find()) {
            String[] connections = matcher.group(1).split(" ");
            for (String connection : connections) {
                String[] parts = connection.split("-");
                if (parts.length != 2) continue; // skip invalid format
                String name = parts[0];
                int pin = Integer.parseInt(parts[1]);

                if (!deviceMap.containsKey(name)) {
                    deviceMap.put(name, createDevice(name));
                }

                if (pin == 1 && !name.equals("VCC") && !name.equals("GND")) {
                    double voltage = deviceMap.containsKey("VCC") ? 220 : 0;
                    deviceMap.get(name).updateVoltage(voltage);
                }
            }
        }
    }

    private static ElectricDevice createDevice(String name) {
        if (name.startsWith("K")) {
            return new Switch(name);
        } else if (name.startsWith("F")) {
            return new FenSpeeder(name);
        } else if (name.startsWith("L")) {
            return new LianSpeeder(name);
        } else if (name.startsWith("B")) {
            return new BaiLamp(name);
        } else if (name.startsWith("R")) {
            return new DayLamp(name);
        } else if (name.startsWith("D")) {
            return new Fan(name);
        }
        throw new IllegalArgumentException("Unknown device type: " + name);
    }
}

SourceMonitor分析结果如下:
image
这段代码主要是一个电子设备控制系统的模拟实现。以下是其设计思路分析:

  1. 抽象类 ElectricDevice:

    • 定义了电子设备的基本属性和方法,如名称、电压以及更新电压状态的抽象方法。
    • 所有具体电子设备类都必须继承自该抽象类,并实现其抽象方法。
  2. 具体电子设备类:

    • Switch:开关类,能够控制设备的开关状态。
    • FenSpeeder:分速器类,可以调节设备的速度等级。
    • LianSpeeder:联速器类,通过设置倍数来调节设备的电压。
    • BaiLamp:白光灯类,根据输入的电压来调节亮度。
    • DayLamp:日光灯类,根据电压状态固定输出亮度。
    • Fan:风扇类,根据输入电压来调节转速。
  3. Main 类:

    • 主要负责用户输入的处理和设备连接。
    • 使用正则表达式处理控制命令,根据不同命令执行相应的操作。
    • 处理设备连接命令,根据连接情况更新设备的电压状态。
    • 最后输出所有设备的状态信息。

题目集6 7-1

7-1 家居强电电路模拟程序-2
分数 100
作者 蔡轲
单位 南昌航空大学
智能家居是在当下家庭中越来越流行的一种配置方案,它通过物联网技术将家中的各种设备(如音视频设备、照明系统、窗帘控制、空调控制、安防系统、数字影院系统、影音服务器、影柜系统、网络家电等)连接到一起,提供家电控制、照明控制、电话远程控制、室内外遥控、防盗报警、环境监测、暖通控制、红外转发以及可编程定时控制等多种功能和手段。与普通家居相比,智能家居不仅具有传统的居住功能,兼备建筑、网络通信、信息家电、设备自动化,提供全方位的信息交互功能。请根据如下要去设计一个智能家居强电电路模拟系统。以下题目介绍中加粗的部分为本次迭代在“家居强电电路模拟程序-1”的基础上增加的功能要求。

1、控制设备

本题模拟的控制设备包括:开关、分档调速器、连续调速器。

开关:包括0和1两种状态。

开关有两个引脚,任意一个引脚都可以是输入引脚,而另一个则是输出引脚。开关状态为0时,无论输入电位是多少,输出引脚电位为0。当开关状态为1时,输出引脚电位等于输入电位。
分档调速器

按档位调整,常见的有3档、4档、5档调速器,档位值从0档-2(3/4)档变化。本次迭代模拟4档调速器,每个档位的输出电位分别为0、0.3、0.6、0.9倍的输入电压。
连续调速器

没有固定档位,按位置比例得到档位参数,数值范围在[0.00-1.00]之间,含两位小数。输出电位为档位参数乘以输入电压。
所有调速器都有两个引脚,一个固定的输入(引脚编号为1)、一个输出引脚(引脚编号为2)。当输入电位为0时,输出引脚输出的电位固定为0,不受各类开关调节的影响。

所有控制设备的初始状态/档位为0。

控制设备的输入引脚编号为1,输出引脚编号为2。
所有开关的电阻为 0。

2、受控设备

本题模拟的受控设备包括:灯、风扇。两种设备都有两根引脚,通过两根引脚电压的电压差驱动设备工作。

灯有两种工作状态:亮、灭。在亮的状态下,有的灯会因引脚电位差的不同亮度会有区别。
风扇在接电后有两种工作状态:停止、转动。风扇的转速会因引脚间电位差的不同而有区别。
本次迭代模拟两种灯具。

白炽灯:

亮度在0~200lux(流明)之间。
电位差为0-9V时亮度为0,其他电位差按比例,电位差10V对应50ux,220V对应200lux,其他电位差与对应亮度值成正比。白炽灯超过220V。
日光灯:

亮度为180lux。
只有两种状态,电位差为0时,亮度为0,电位差不为0,亮度为180。
本次迭代模拟一种吊扇。

工作电压区间为80V-150V,对应转速区间为80-360转/分钟。80V对应转速为80转/分钟,150V对应转速为360转/分钟,超过150V转速为360转/分钟(本次迭代暂不考虑电压超标的异常情况)。其他电压值与转速成正比,输入输出电位差小于80V时转速为0。
本次迭代模拟一种落地扇。

工作电压区间为 [80V,150V],对应转速区间为 80-360 转/分钟。电压在[80,100)V 区间对应转速为 80 转/分 钟,[100-120)V 区间对应转速为 160 转/分钟,[120-140)V 区间对应转速为 260 转/分钟,超过 140V 转速 为 360 转/分钟(本次迭代暂不考虑电压超标的异常情况)输入信息:
本次迭代考虑电阻:白炽灯的电阻为 10,日光灯的电阻为 5,吊扇的电阻为 20,落 地扇的电阻为 20

3、输入信息

1)输入设备信息

分别用设备标识符K、F、L、B、R、D、A分别表示开关、分档调速器、连续调速器、白炽灯、日光灯、吊扇、落地扇。

设备标识用标识符+编号表示,如K1、F3、L2等。
引脚格式:设备标识-引脚编号,例如:K1-1标识编号为1的开关的输入引脚。

三种控制开关的输入引脚编号为1,输出引脚编号为2。
受控设备的两个引脚编号分别为1、2。
约束条件:

不同设备的编号可以相同。
同种设备的编号可以不连续。
设备信息不单独输入,包含在连接信息中。

2)输入连接信息

一条连接信息占一行,用[]表示一组连接在一起的设备引脚,引脚与引脚之间用英文空格" "分隔。

格式:"["+引脚号+" "+...+" "+引脚号+"]"
例如:[K1-1 K3-2 D5-1]表示K1的输入引脚,K3的输出引脚,D5的1号引脚连接在一起。
约束条件:

不考虑调速器串联到其他调速器的情况。
不考虑调速器串联到其他调速器的情况。
考虑各类设备的并联接入。例如,K1 的输出接到 L2 的输入,L2 的输出再接其他设备属于串联接线。K1 的输出接到 L2 的输出,同时 K1 的输入接到 L2 的输入,这种情况属于并联。
本次迭代的连接信息不单独输入,包含在线路信息中。

3)输入控制设备调节信息

开关调节信息格式:

//#+设备标识K+设备编号,例如:#K2,代表切换K2开关的状态。
分档调速器的调节信息格式:

//#+设备标识F+设备编号+"+" 代表加一档,例如:#F3+,代表F3输出加一档。
//#+设备标识F+设备编号+"-" 代表减一档,例如:#F1-,代表F1输出减一档。
连续调速器的调节信息格式:

//#+设备标识L+设备编号+":" +数值 代表将连续调速器的档位设置到对应数值,例如:#L3:0.6,代表L3输出档位参数0.6。
4)电源接地标识:

VCC,电压220V,GND,电压0V。没有接线的引脚默认接地,电压为0V。

5)输入串联电路信息

一条串联电路占一行,串联电路由按从靠电源端到接地端顺序依次输入的 n 个连接 信息组成,连接信息之间用英文空格" "分隔。

串联电路信息格式:

"#T"+电路编号+":"+连接信息+" "+连接信息+...+" "+连接信息
例如:#T1:[IN K1-1] [K1-2 D2-1] [D2-2 OUT] 一个串联电路的第一个引脚是 IN,代表起始端,靠电源。最后一个引脚是 OUT,代表结尾端, 靠接地。
约束条件:

不同的串联电路信息编号不同。
输入的最后一条电路信息必定是总电路信息,总电路信息的起始引脚是 VCC,结束引脚是 GND。
连接信息中的引脚可能是一条串联或并联电路的 IN 或者 OUT。例如:
//#T1:[IN K1-1] [K1-2 T2-IN] [T2-OUT OUT]
//#T1:[IN K1-1] [K1-2 T2-IN] [T2-OUT M2-IN] [M2-OUT OUT]

6)输入并联电路信息

一条并联电路占一行,并联电路由其包含的几条串联电路组成,串联电路标识之间用英文空格" "分隔。

格式:

"#M"+电路编号+":"+”[”+串联电路信息+" "+....+" "+串联电路信息+”]”
例如:#M1:[T1 T2 T3]
该例声明了一个并联电路,由 T1、T2、T3 三条串联电路并联而成,三条串联电路的 IN 短 接在一起构成 M1 的 IN,三条串联电路的 OUT 短接在一起构成 M1 的 OUT。
约束条件:

本次迭代不考虑并联电路中包含并联电路的情况,也不考虑多个并联电路串联的情况。
本题不考虑输入电压或电压差超过220V的情况。

输入信息以end为结束标志,忽略end之后的输入信息。

本题中的并联信息所包含的串联电路的信息都在并联信息之前输入,不考虑乱序输入的情况。
电路中的短路如果不会在电路中产生无穷大的电流烧坏电路,都是合理情况,在本题测试点的考虑范围之内。

本题不考虑一条串联电路中包含其他串联电路的情况。例如:

//#T3:[VCC K1-1] [K1-2 T2-IN] [T2-OUT K2-1] [K2-2 T1-IN] [T1-OUT GND]
本例中T1\T2两条串联电路实际是T3的一个部分,本题不考虑这种类型的输入,而是当将T1\T2的所有连接信息直接包含在T3中定义。
下次迭代中需要考虑这种类型的输入。
4、输出信息:

按开关、分档调速器、连续调速器、白炽灯、日光灯、吊扇、落地扇的顺序依次输出所有设备的状态或参数。每个设备一行。同类设备按编号顺序从小到大输出。

输出格式:@设备标识+设备编号+":" +设备参数值(控制开关的档位或状态、灯的亮度、风扇的转速,只输出值,不输出单位)
连续调速器的档位信息保留两位小数,即使小数为0,依然显示两位小数.00。
开关状态为0(打开)时显示turned on,状态为1(合上)时显示closed
如:
@K1:turned on
@B1:190
@L1:0.60
5、家居电路模拟系列所有题目的默认规则:

1)当计算电压值等数值的过程中,最终结果出现小数时,用截尾规则去掉小数部分,只保留整数部分。为避免精度的误差,所有有可能出现小数的数值用double类型保存并计算,不要作下转型数据类型转换,例如电压、转速、亮度等,只有在最后输出时再把计算结果按截尾规则,舍弃尾数,保留整数输出。

2)所有连接信息按电路从电源到接地的顺序依次输入,不会出现错位的情况。电源VCC一定是第一个连接的第一项,接地GND一定是最后一个连接的后一项。

3)连接信息如果只包含两个引脚,靠电源端的引脚在前,靠接地端的在后。

4)调速器的输入端只会直连VCC,不会接其他设备。整个电路最多只有连接在电源上的一个调速器,且不包含在并联单路中。

6、家居电路模拟系列1-4题目后续迭代设计:

1)电路结构变化:

迭代1:只有一条线路,所有元件串联
迭代2:线路中包含一个并联电路
迭代3:线路中包含多个串联起来的并联电路
迭代4:并联电路之间可能出现包含关系

电路结构变化示意图见图1。

2)计算方式的变化

迭代1只包含1个受控元件,不用计算电流,之后的电路计算要包含电流、电阻等电路参数。

3)电路元件的变化

每次迭代会增加1-2个新的电路元件。

image

图1:电路结构示意图

设计建议:

1、电路设备类:描述所有电路设备的公共特征。

2、受控设备类、控制设备类:对应受控、控制设备

3、串联电路类:一条由多个电路设备构成的串联电路,也看成是一个独立的电路设备

4、并联电路类:继承电路设备类,也看成是一个独立的电路设备

其他类以及类的属性、方法自行设计。

image

图2:建议设计类图

输入格式:
请在这里写输入格式。例如:输入在一行中给出2个绝对值不超过1000的整数A和B。

输出格式:
请在这里描述输出格式。例如:对每一组输入,在一行中输出A+B的值。

输入样例1:
在这里给出一组输入。例如:

//#T1:[IN K1-1] [K1-2 D2-1] [D2-2 OUT]
//#T2:[IN K2-1] [K2-2 D1-1] [D1-2 OUT]
//#M1:[T1 T2]
//#T3:[VCC L1-1] [L1-2 M1-IN] [M1-OUT D3-1] [D3-2 GND]
//#K1
//end
输出样例1:
在这里给出相应的输出。例如:

@K1:closed
@K2:turned on
@L1:0.00
@D1:0
@D2:0
@D3:0
输入样例2:
在这里给出一组输入。例如:

//#T1:[IN K1-1] [K1-2 D2-1] [D2-2 OUT]
//#T2:[IN K2-1] [K2-2 D1-1] [D1-2 OUT]
//#M1:[T1 T2]
//#T3:[VCC L1-1] [L1-2 M1-IN] [M1-OUT D3-1] [D3-2 GND]
//#K1
//#L1:1.00
//end
输出样例2:
在这里给出相应的输出。例如:

@K1:closed
@K2:turned on
@L1:1.00
@D1:0
@D2:200
@D3:200
输入样例3:
在这里给出一组输入。例如:

//#T1:[IN K1-1] [K1-2 D2-1] [D2-2 OUT]
//#T2:[IN K2-1] [K2-2 D1-1] [D1-2 OUT]
//#M1:[T1 T2]
//#T3:[VCC L1-1] [L1-2 M1-IN] [M1-OUT D3-1] [D3-2 GND]
//#K1
//#K2
//#L1:1.00
//end
输出样例3:
在这里给出相应的输出。例如:

@K1:closed
@K2:closed
@L1:1.00
@D1:0
@D2:0
@D3:346
代码长度限制
40 KB
时间限制
1000 ms
内存限制
64 MB
栈限制
8192 KB

代码如下:

import java.util.*;
import java.util.regex.*;

abstract class CircuitDevice {
    String deviceId;
    Map<Integer, Double> inputs = new HashMap<>();
    Map<Integer, Double> outputs = new HashMap<>();

    CircuitDevice(String deviceId) {
        this.deviceId = deviceId;
    }

    void addInput(int pin, double voltage) {
        inputs.put(pin, voltage);
    }

    void addOutput(int pin, double voltage) {
        outputs.put(pin, voltage);
    }

    abstract void updateVoltage();
}

class Switch extends CircuitDevice {
    int state = 0; // 0: turned on, 1: closed

    Switch(String deviceId) {
        super(deviceId);
    }

    void toggle() {
        state = 1 - state;
        updateVoltage();
    }

    @Override
    void updateVoltage() {
        if (state == 1) {
            for (int pin : outputs.keySet()) {
                outputs.put(pin, inputs.getOrDefault(1, 0.0));
            }
        } else {
            for (int pin : outputs.keySet()) {
                outputs.put(pin, 0.0);
            }
        }
    }
}

abstract class SpeedController extends CircuitDevice {
    int steps = 0;
    int maxSteps;

    SpeedController(String deviceId, int maxSteps) {
        super(deviceId);
        this.maxSteps = maxSteps;
    }

    void increaseStep() {
        if (steps < maxSteps) {
            steps++;
        }
        updateVoltage();
    }

    void decreaseStep() {
        if (steps > 0) {
            steps--;
        }
        updateVoltage();
    }

    abstract void updateVoltage();
}

class StepSpeedController extends SpeedController {
    StepSpeedController(String deviceId) {
        super(deviceId, 3);
    }

    @Override
    void updateVoltage() {
        double[] voltageRatios = {0, 0.3, 0.6, 0.9};
        for (int pin : outputs.keySet()) {
            outputs.put(pin, voltageRatios[steps] * inputs.getOrDefault(1, 0.0));
        }
    }
}

class ContinuousSpeedController extends SpeedController {
    ContinuousSpeedController(String deviceId) {
        super(deviceId, 100); // 0.00-1.00 * 100
    }

    void setStep(double stepValue) {
        steps = (int) (stepValue * 100);
        updateVoltage();
    }

    @Override
    void updateVoltage() {
        for (int pin : outputs.keySet()) {
            outputs.put(pin, (steps / 100.0) * inputs.getOrDefault(1, 0.0));
        }
    }
}

abstract class Light extends CircuitDevice {
    int brightness = 0;

    Light(String deviceId) {
        super(deviceId);
    }

    abstract void updateBrightness();
}

class IncandescentLight extends Light {
    IncandescentLight(String deviceId) {
        super(deviceId);
    }

    @Override
    void updateBrightness() {
        double voltageDiff = inputs.getOrDefault(1, 0.0) - inputs.getOrDefault(2, 0.0);
        if (voltageDiff > 220) {
            voltageDiff = 220;
        }
        brightness = (int) (voltageDiff * (200 / 220.0));
    }

    @Override
    void updateVoltage() {
        updateBrightness();
    }
}

class FluorescentLight extends Light {
    FluorescentLight(String deviceId) {
        super(deviceId);
    }

    @Override
    void updateBrightness() {
        double voltageDiff = inputs.getOrDefault(1, 0.0) - inputs.getOrDefault(2, 0.0);
        brightness = voltageDiff != 0 ? 180 : 0;
    }

    @Override
    void updateVoltage() {
        updateBrightness();
    }
}

abstract class Fan extends CircuitDevice {
    int speed = 0;

    Fan(String deviceId) {
        super(deviceId);
    }

    abstract void updateSpeed();
}

class CeilingFan extends Fan {
    CeilingFan(String deviceId) {
        super(deviceId);
    }

    @Override
    void updateSpeed() {
        double voltageDiff = inputs.getOrDefault(1, 0.0) - inputs.getOrDefault(2, 0.0);
        if (voltageDiff > 150) {
            voltageDiff = 150;
        }
        speed = voltageDiff >= 80 ? (int) ((voltageDiff - 80) * (280 / 70.0)) + 80 : 0;
    }

    @Override
    void updateVoltage() {
        updateSpeed();
    }
}

class FloorFan extends Fan {
    FloorFan(String deviceId) {
        super(deviceId);
    }

    @Override
    void updateSpeed() {
        double voltageDiff = inputs.getOrDefault(1, 0.0) - inputs.getOrDefault(2, 0.0);
        if (voltageDiff >= 140) {
            speed = 360;
        } else if (voltageDiff >= 120) {
            speed = 260;
        } else if (voltageDiff >= 100) {
            speed = 160;
        } else if (voltageDiff >= 80) {
            speed = 80;
        } else {
            speed = 0;
        }
    }

    @Override
    void updateVoltage() {
        updateSpeed();
    }
}

class SeriesCircuit {
    String circuitId;
    List<CircuitDevice> devices = new ArrayList<>();

    SeriesCircuit(String circuitId) {
        this.circuitId = circuitId;
    }

    void addDevice(CircuitDevice device) {
        devices.add(device);
    }

    void updateCircuit() {
        for (CircuitDevice device : devices) {
            device.updateVoltage();
        }
    }
}

class ParallelCircuit {
    String circuitId;
    List<SeriesCircuit> seriesCircuits = new ArrayList<>();

    ParallelCircuit(String circuitId) {
        this.circuitId = circuitId;
    }

    void addSeriesCircuit(SeriesCircuit seriesCircuit) {
        seriesCircuits.add(seriesCircuit);
    }

    void updateCircuit() {
        for (SeriesCircuit seriesCircuit : seriesCircuits) {
            seriesCircuit.updateCircuit();
        }
    }
}

public class Main {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        List<String> inputLines = new ArrayList<>();
        String line;
        while (!(line = scanner.nextLine()).equals("end")) {
            inputLines.add(line);
        }
        scanner.close();

        Map<String, SeriesCircuit> seriesCircuits = new HashMap<>();
        Map<String, ParallelCircuit> parallelCircuits = new HashMap<>();
        Map<String, CircuitDevice> devices = new HashMap<>();

        parseInput(inputLines, seriesCircuits, parallelCircuits, devices);

        for (SeriesCircuit circuit : seriesCircuits.values()) {
            circuit.updateCircuit();
        }
        for (ParallelCircuit circuit : parallelCircuits.values()) {
            circuit.updateCircuit();
        }

        outputResults(devices);
    }

    private static void parseInput(List<String> inputLines, Map<String, SeriesCircuit> seriesCircuits, Map<String, ParallelCircuit> parallelCircuits, Map<String, CircuitDevice> devices) {
        Pattern seriesPattern = Pattern.compile("#T(\\d+)");
        Pattern parallelPattern = Pattern.compile("#M(\\d+)");
        Pattern devicePattern = Pattern.compile("(\\w+)-(\\d+)");
        Pattern controlPattern = Pattern.compile("#(\\w+(:?\\d+\\.\\d+)?)");

        for (String line : inputLines) {
            if (line.startsWith("#T")) {
                Matcher seriesMatcher = seriesPattern.matcher(line);
                if (seriesMatcher.find()) {
                    String circuitId = seriesMatcher.group(1);
                    SeriesCircuit seriesCircuit = new SeriesCircuit(circuitId);
                    seriesCircuits.put(circuitId, seriesCircuit);
                    Matcher deviceMatcher = devicePattern.matcher(line);
                    while (deviceMatcher.find()) {
                        String deviceId = deviceMatcher.group(1);
                        if (!devices.containsKey(deviceId)) {
                            devices.put(deviceId, createDevice(deviceId));
                        }
                        seriesCircuit.addDevice(devices.get(deviceId));
                    }
                }
            } else if (line.startsWith("#M")) {
                Matcher parallelMatcher = parallelPattern.matcher(line);
                if (parallelMatcher.find()) {
                    String circuitId = parallelMatcher.group(1);
                    ParallelCircuit parallelCircuit = new ParallelCircuit(circuitId);
                    parallelCircuits.put(circuitId, parallelCircuit);
                    Matcher seriesMatcher = seriesPattern.matcher(line);
                    while (seriesMatcher.find()) {
                        String seriesId = seriesMatcher.group(1);
                        parallelCircuit.addSeriesCircuit(seriesCircuits.get(seriesId));
                    }
                }
            } else if (line.startsWith("#")) {
                Matcher controlMatcher = controlPattern.matcher(line);
                if (controlMatcher.find()) {
                    String controlInfo = controlMatcher.group(1);
                    applyControl(devices, controlInfo);
                }
            }
        }
    }

    private static CircuitDevice createDevice(String deviceId) {
        if (deviceId.startsWith("K")) {
            return new Switch(deviceId);
        } else if (deviceId.startsWith("F")) {
            return new StepSpeedController(deviceId);
        } else if (deviceId.startsWith("L")) {
            return new ContinuousSpeedController(deviceId);
        } else if (deviceId.startsWith("B")) {
            return new IncandescentLight(deviceId);
        } else if (deviceId.startsWith("R")) {
            return new FluorescentLight(deviceId);
        } else if (deviceId.startsWith("D")) {
            return new CeilingFan(deviceId);
        } else if (deviceId.startsWith("A")) {
            return new FloorFan(deviceId);
        } else {
            throw new IllegalArgumentException("Unknown device type: " + deviceId);
        }
    }

    private static void applyControl(Map<String, CircuitDevice> devices, String controlInfo) {
        if (controlInfo.startsWith("K")) {
            ((Switch) devices.get(controlInfo)).toggle();
        } else if (controlInfo.startsWith("F")) {
            if (controlInfo.endsWith("+")) {
                ((SpeedController) devices.get(controlInfo.substring(0, controlInfo.length() - 1))).increaseStep();
            } else if (controlInfo.endsWith("-")) {
                ((SpeedController) devices.get(controlInfo.substring(0, controlInfo.length() - 1))).decreaseStep();
            }
        } else if (controlInfo.startsWith("L")) {
            String[] parts = controlInfo.split(":");
            ((ContinuousSpeedController) devices.get(parts[0])).setStep(Double.parseDouble(parts[1]));
        }
    }

    private static void outputResults(Map<String, CircuitDevice> devices) {
        for (CircuitDevice device : devices.values()) {
            if (device instanceof Switch) {
                String state = ((Switch) device).state == 1 ? "closed" : "turned on";
                System.out.println("@" + device.deviceId + ":" + state);
            }
        }


        for (CircuitDevice device : devices.values()) {
            if (device instanceof Light) {
                ((Light) device).updateBrightness();
                System.out.println("@" + device.deviceId + ":" + ((Light) device).brightness);
            }
        }

        for (CircuitDevice device : devices.values()) {
            if (!(device instanceof Switch) && !(device instanceof Light)) {
                if (device instanceof SpeedController) {
                    System.out.printf("@%s:%.2f%n", device.deviceId, ((SpeedController) device).steps / 100.0);
                } else if (device instanceof Fan) {
                    ((Fan) device).updateSpeed();
                    System.out.println("@" + device.deviceId + ":" + ((Fan) device).speed);
                }
            }
        }
    }
}

SourceMonitor分析结果如下:
image
这段代码实现了一个电路模拟系统,其中包括了电路设备、开关、速度控制器、灯光和风扇等组件,并且支持串联和并联电路的构建和控制。

  1. 抽象类 CircuitDevice: 定义了电路设备的基本属性和方法,包括设备编号、输入输出电压的记录以及电压更新方法。

  2. 具体设备类 Switch、StepSpeedController、IncandescentLight 等: 分别表示开关、步进速度控制器、白炽灯等具体的电路设备,实现了各自特定的功能和电压更新逻辑。

  3. 串联电路类 SeriesCircuit 和并联电路类 ParallelCircuit: 分别表示串联和并联电路,可以管理多个电路设备,并实现了电路设备状态的更新。

  4. Main 类: 包含了程序的主逻辑,负责解析输入、构建电路、应用控制指令以及输出结果。

在设计思路上,这段代码实现了一种模拟电路的构建和控制方式。通过抽象类和继承实现了电路设备的统一管理,通过串联和并联电路类实现了电路的构建和控制,同时通过具体设备类实现了各种特定功能的电路组件。

(3)采坑心得

1,对于题目集4的7-1,程序的主要逻辑在processAnswers 方法中实现,其中对学生答案进行了评分,并输出了评分结果。虽然实现了基本功能,但可能存在一些潜在的逻辑漏洞和边界情况没有考虑到,比如对于不同类型的题目(单选、多选、填空)可能需要采取不同的评分策略,对于无效题目的处理等。程序使用了多个 Map 和 List 来存储试题、试卷、学生信息和答案等数据,这样做在一定程度上简化了逻辑实现,但也增加了代码的复杂度。程序对输入的格式进行了基本的处理,但在处理输入时可能存在一些潜在的问题,比如对于不符合规范的输入格式,程序只是简单地输出"wrong format",而没有给出具体的错误信息和位置,这可能会使得调试困难。在代码中使用了 try-catch 块来捕获可能出现的异常,但捕获异常后只是简单地输出错误信息,而没有采取进一步的处理措施。在实际应用中,应该根据具体情况采取合适的异常处理策略,比如记录日志、给出友好的错误提示等。
2.对于题目集5的7-1,在处理控制命令时,使用了强制类型转换,如 (Switch) deviceMap.get(name)。如果 deviceMap.get(name) 返回的不是 Switch 类型的对象,会导致 ClassCastException。可以通过使用 instanceof 进行类型检查,或者使用更安全的方法来处理。在处理控制命令时,使用了正则表达式进行匹配,但是正则表达式的匹配可能不够精确,导致一些非法命令被误认为是有效命令。可以通过增强正则表达式的匹配规则,提高匹配的准确性。在处理设备连接命令时,使用了正则表达式和字符串分割来解析输入,这种方法可能会对输入的格式有一定的要求,如果输入格式不符合预期,会导致解析错误。可以增加输入格式的验证机制,以提高代码的健壮性。在代码中并未充分考虑异常情况的处理,比如输入格式错误、设备不存在等情况。可以增加相应的异常处理逻辑,以提高代码的健壮性和容错性。
3.对于题目集6的7-1,在输入解析部分和设备控制部分,没有对可能出现的异常情况进行处理,比如输入格式错误、设备不存在等情况。应该加入异常处理机制,增强代码的健壮性。有一些代码逻辑可能存在重复,比如在更新电压时,部分设备类的逻辑相似,可以考虑提取公共部分,减少重复代码。目前的代码结构虽然清晰,但可能不够灵活和可扩展。比如,如果要添加新的设备类型或者调整现有设备的属性,可能需要修改大量的代码。可以考虑使用设计模式来提高代码的灵活性和可扩展性。缺少针对不同情况的测试,可能导致程序在特定情况下出现未知的错误。应该编写全面的测试用例,覆盖各种输入情况和边界情况,确保程序的稳定性和正确性。

(4)改进建议

1.改进题目集4的7-1,将 main 方法中的逻辑分解成更小的函数或方法,每个函数负责一个具体的功能,例如解析输入、添加题目、添加试卷、添加学生、添加答案等。在 addQuestion, addPaper, addStudents, addAnswer, deleteQuestion 方法中增加异常处理机制,对可能出现的异常情况进行捕获和处理,例如格式错误或越界异常等。可以考虑使用更高效的数据结构和算法来提高代码的性能,例如使用 StringBuilder 替代字符串拼接操作,或者使用 LinkedHashMap 保持题目和试卷的顺序。 在 processAnswers 方法中,对可能出现的异常情况进行处理,例如试卷不存在或学生不存在等情况,给出相应的错误提示并采取适当的措施处理异常。循 Java 的命名规范,类名应该首字母大写,方法名和变量名应该采用驼峰命名法,清晰明了地表达意思。
2.改进题目集5的7-1,在 handleControlCommandhandleConnectionCommand 方法中,添加适当的异常处理机制,如 try-catch 块,以捕获可能的异常并进行处理。将处理输入和设备控制命令的逻辑封装成单独的方法,例如 processInputprocessControlCommand,以提高代码的可读性和可维护性。在处理设备连接命令时,添加输入验证机制,例如使用正则表达式或条件检查来确保输入格式的正确性。将一些通用的逻辑提取到单独的方法中,以避免重复编写相似的代码,比如将设备创建逻辑中的类型判断提取为单独的方法或工具类。编写单元测试来验证每个方法的功能和逻辑是否正确,包括处理输入、设备控制和设备连接等逻辑。查看代码中是否有性能瓶颈或不必要的重复计算,并尝试优化这些部分以提高代码的效率。
3.改进题目集6的7-1,在解析输入和设备控制部分,应该添加异常处理机制,捕获可能出现的异常情况,例如输入格式错误、设备不存在等,以提高代码的健壮性和稳定性。确保变量、方法和类的命名清晰、具有描述性,方便他人阅读和理解代码。避免使用缩写或不明确的命名,提高代码的可维护性。将公共逻辑提取到单独的方法或父类中,避免代码重复。这样可以提高代码的复用性和可维护性,同时减少潜在的错误。编写全面的测试用例,覆盖各种输入情况和边界情况,以确保程序的稳定性和正确性。可以使用单元测试、集成测试等方法来验证代码的功能和逻辑。虑使用设计模式,如工厂模式、策略模式等,来提高代码的灵活性和可扩展性。这样可以轻松地添加新的设备类型或调整现有设备的属性,而无需修改大量的代码。

(5)总结

通过本阶段的三次题目集的练习,我对面向对象编程的认识提升了不少,编写抽象类和接口可以帮助我更好地理解面向对象编程的基本原理和设计思想。通过定义抽象类和方法,我可以实现代码的可重用性和可扩展性,从而更轻松地应对需求变化和新功能的添加。重复的代码会增加维护成本和代码的复杂性,因此需要尽可能地重用代码,并对代码进行优化以提高性能。通过提取通用的逻辑和优化重复计算,可以使代码更加简洁高效。同时我还学会了使用正则表达式来匹配和解析输入,提高了代码的灵活性和扩展性。代码编写过程中,我意识到了一些设计模式的应用场景,例如模板方法模式、策略模式等。通过合理地运用设计模式,我可以将复杂的问题分解为简单的组件,降低代码的耦合度,提高代码的灵活性和可维护性。但在编写代码过程中,我也发现了自己的不足,我必须考虑到可能出现的异常情况,并实施适当的异常处理机制,这可以确保程序在面对异常情况时能够正常运行或提供合适的反馈,增强了代码的健壮性。我还应该重视编写测试用例,并善于使用调试工具进行调试和排查问题。这有助于我发现代码中的潜在问题,并及时进行修复,从而提高代码的质量和可靠性。编写代码是解决问题的过程,我学会了如何将一个大问题分解成更小的、可管理的部分,并逐步解决每个部分,最终完成整个任务。编写代码往往需要花费大量的时间和精力,尤其是在调试和解决 bug 的过程中,我学会了保持耐心和毅力,不断尝试和改进。总的来说,通过这三次题目集的练习,我不仅学会了如何应用面向对象编程的基本原理和设计模式,还提高了对代码质量和可维护性的认识,从而更好地应对实际开发中的各种挑战和需求。

posted on 2024-06-05 14:02  23201537周兴  阅读(80)  评论(0)    收藏  举报