面向对象(OOP,OOD) Java 题解(入门级、普及级) (from 黄老师)

以下完整内容,包含题目名称、题号、洛谷链接和对应的面向对象Java题解:

一、P1001 A+B Problem

洛谷链接: https://www.luogu.com.cn/problem/P1001
面向对象适配点: 可封装Calculator类,用方法实现加法逻辑,入门级OOP实践

import java.util.Scanner;

class Calculator {
    private int a;
    private int b;
    
    public Calculator(int a, int b) {
        this.a = a;
        this.b = b;
    }
    
    public int add() {
        return this.a + this.b;
    }
}

public class Main {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        int a = scanner.nextInt();
        int b = scanner.nextInt();
        
        Calculator calculator = new Calculator(a, b);
        System.out.println(calculator.add());
        
        scanner.close();
    }
}

二、P1047 校门外的树

洛谷链接: https://www.luogu.com.cn/problem/P1047
面向对象适配点: 封装TreeZone类,管理区间、实现砍树/统计逻辑,体现封装特性

import java.util.Scanner;

class TreeZone {
    private boolean[] trees;
    
    public TreeZone(int length) {
        trees = new boolean[length + 1];
        for (int i = 0; i <= length; i++) {
            trees[i] = true; // true表示树还在
        }
    }
    
    public void removeTrees(int start, int end) {
        for (int i = start; i <= end; i++) {
            trees[i] = false; // false表示树被移除了
        }
    }
    
    public int countRemainingTrees() {
        int count = 0;
        for (boolean tree : trees) {
            if (tree) {
                count++;
            }
        }
        return count;
    }
}

public class Main {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        int L = scanner.nextInt();
        int M = scanner.nextInt();
        
        TreeZone treeZone = new TreeZone(L);
        
        for (int i = 0; i < M; i++) {
            int start = scanner.nextInt();
            int end = scanner.nextInt();
            treeZone.removeTrees(start, end);
        }
        
        System.out.println(treeZone.countRemainingTrees());
        scanner.close();
    }
}

三、P1068 分数线划定

洛谷链接: https://www.luogu.com.cn/problem/P1068
面向对象适配点: 封装"考生"为类,实现Comparable接口

import java.util.*;

class Candidate implements Comparable<Candidate> {
    private int id;
    private int score;
    
    public Candidate(int id, int score) {
        this.id = id;
        this.score = score;
    }
    
    public int getId() { return id; }
    public int getScore() { return score; }
    
    @Override
    public int compareTo(Candidate other) {
        // 按分数降序,分数相同按学号升序
        if (this.score != other.score) {
            return other.score - this.score;
        }
        return this.id - other.id;
    }
    
    @Override
    public String toString() {
        return id + " " + score;
    }
}

public class Main {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        int n = scanner.nextInt();
        int m = scanner.nextInt();
        
        List<Candidate> candidates = new ArrayList<>();
        
        for (int i = 0; i < n; i++) {
            int id = scanner.nextInt();
            int score = scanner.nextInt();
            candidates.add(new Candidate(id, score));
        }
        
        Collections.sort(candidates);
        
        int interviewCount = (int)(m * 1.5);
        int scoreLine = candidates.get(interviewCount - 1).getScore();
        
        // 统计实际进入面试的人数
        int actualCount = 0;
        for (Candidate c : candidates) {
            if (c.getScore() >= scoreLine) {
                actualCount++;
            }
        }
        
        System.out.println(scoreLine + " " + actualCount);
        for (int i = 0; i < actualCount; i++) {
            System.out.println(candidates.get(i));
        }
        
        scanner.close();
    }
}

四、P1093 奖学金

洛谷链接: https://www.luogu.com.cn/problem/P1093
面向对象适配点: 封装学生对象,自定义比较器(经典OOP排序题)

import java.util.*;

class Student implements Comparable<Student> {
    private int id;
    private int chinese;
    private int math;
    private int english;
    private int total;
    
    public Student(int id, int chinese, int math, int english) {
        this.id = id;
        this.chinese = chinese;
        this.math = math;
        this.english = english;
        this.total = chinese + math + english;
    }
    
    public int getId() { return id; }
    public int getChinese() { return chinese; }
    public int getTotal() { return total; }
    
    @Override
    public int compareTo(Student other) {
        // 按总分降序
        if (this.total != other.total) {
            return other.total - this.total;
        }
        // 总分相同按语文降序
        if (this.chinese != other.chinese) {
            return other.chinese - this.chinese;
        }
        // 总分和语文都相同按学号升序
        return this.id - other.id;
    }
    
    @Override
    public String toString() {
        return id + " " + total;
    }
}

public class Main {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        int n = scanner.nextInt();
        
        List<Student> students = new ArrayList<>();
        
        for (int i = 1; i <= n; i++) {
            int chinese = scanner.nextInt();
            int math = scanner.nextInt();
            int english = scanner.nextInt();
            students.add(new Student(i, chinese, math, english));
        }
        
        Collections.sort(students);
        
        // 输出前5名
        for (int i = 0; i < Math.min(5, students.size()); i++) {
            System.out.println(students.get(i));
        }
        
        scanner.close();
    }
}

五、P1603 斯诺登的密码

洛谷链接: https://www.luogu.com.cn/problem/P1603
面向对象适配点: 封装单词-数字映射类

import java.util.*;

class WordToNumber {
    private static final Map<String, Integer> wordMap = new HashMap<>();
    
    static {
        // 初始化单词到数字的映射
        wordMap.put("one", 1); wordMap.put("two", 4); wordMap.put("three", 9);
        wordMap.put("four", 16); wordMap.put("five", 25); wordMap.put("six", 36);
        wordMap.put("seven", 49); wordMap.put("eight", 64); wordMap.put("nine", 81);
        wordMap.put("ten", 0); wordMap.put("eleven", 21); wordMap.put("twelve", 44);
        wordMap.put("thirteen", 69); wordMap.put("fourteen", 96); wordMap.put("fifteen", 25);
        wordMap.put("sixteen", 56); wordMap.put("seventeen", 89); wordMap.put("eighteen", 24);
        wordMap.put("nineteen", 61); wordMap.put("twenty", 0); wordMap.put("a", 1);
        wordMap.put("both", 4); wordMap.put("another", 1); wordMap.put("first", 1);
        wordMap.put("second", 4); wordMap.put("third", 9);
    }
    
    private List<Integer> numbers;
    
    public WordToNumber(String input) {
        numbers = new ArrayList<>();
        processInput(input);
    }
    
    private void processInput(String input) {
        String[] words = input.toLowerCase().split("\\s+|[.]");
        
        for (String word : words) {
            if (wordMap.containsKey(word)) {
                int num = wordMap.get(word);
                numbers.add(num);
            }
        }
    }
    
    public String getPassword() {
        if (numbers.isEmpty()) {
            return "0";
        }
        
        // 将数字转换为两位数格式并排序
        List<String> formattedNumbers = new ArrayList<>();
        for (int num : numbers) {
            formattedNumbers.add(String.format("%02d", num));
        }
        
        Collections.sort(formattedNumbers);
        
        // 拼接成密码
        StringBuilder password = new StringBuilder();
        for (String numStr : formattedNumbers) {
            password.append(numStr);
        }
        
        // 移除前导零
        String result = password.toString();
        while (result.length() > 1 && result.charAt(0) == '0') {
            result = result.substring(1);
        }
        
        return result;
    }
}

public class Main {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        String input = scanner.nextLine();
        
        WordToNumber converter = new WordToNumber(input);
        System.out.println(converter.getPassword());
        
        scanner.close();
    }
}

六、P3741 honoka的键盘

洛谷链接: https://www.luogu.com.cn/problem/P3741
面向对象适配点: 封装键盘状态对象进行字符串操作

面向对象设计思路

  1. 类设计:创建VKStringOptimizer类,封装字符串数据和相关操作方法。

  2. 封装性:将字符串长度、字符串内容以及核心算法封装在类内部,通过公共方法提供接口。

  3. 算法逻辑:
    • 先计算原始字符串中不重叠的VK出现次数。

    • 尝试改变每个位置的字符(V↔K),计算新字符串的VK出现次数,取最大值。

    • 由于字符串长度n ≤ 100,直接模拟可行。

import java.util.Scanner;

/**
 * VK字符串优化器类
 * 用于计算通过改变至多一个字符后,字符串中VK出现的最大次数
 */
class VKStringOptimizer {
    private int n;
    private String str;

    /**
     * 构造函数
     * @param n 字符串长度
     * @param str 字符串内容
     */
    public VKStringOptimizer(int n, String str) {
        this.n = n;
        this.str = str;
    }

    /**
     * 计算字符串中不重叠VK出现的次数
     * @param s 输入字符串
     * @return VK出现次数
     */
    private int countVK(String s) {
        int count = 0;
        char[] arr = s.toCharArray();
        for (int i = 0; i < arr.length - 1; i++) {
            if (arr[i] == 'V' && arr[i + 1] == 'K') {
                count++;
                i++; // 跳过下一个字符,避免重叠计数
            }
        }
        return count;
    }

    /**
     * 计算最大可能的VK出现次数
     * @return 最大次数
     */
    public int calculateMaxVK() {
        int baseCount = countVK(str); // 原始VK数量
        int maxCount = baseCount;

        // 如果字符串长度为1,无法形成VK,直接返回
        if (n <= 1) {
            return 0;
        }

        // 尝试改变每个位置的字符
        char[] arr = str.toCharArray();
        for (int i = 0; i < n; i++) {
            // 保存原字符
            char originalChar = arr[i];
            // 改变字符:V->K 或 K->V
            if (originalChar == 'V') {
                arr[i] = 'K';
            } else {
                arr[i] = 'V';
            }

            // 构建新字符串并计算VK数量
            String newStr = new String(arr);
            int newCount = countVK(newStr);
            maxCount = Math.max(maxCount, newCount);

            // 恢复原字符
            arr[i] = originalChar;
        }

        return maxCount;
    }
}

/**
 * 主程序类
 */
public class Main {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        int n = scanner.nextInt();
        String s = scanner.next();

        // 创建VK优化器对象
        VKStringOptimizer optimizer = new VKStringOptimizer(n, s);
        int result = optimizer.calculateMaxVK();

        System.out.println(result);
        scanner.close();
    }
}

代码说明

  1. VKStringOptimizer类:
    • 属性:n(字符串长度)和str(字符串内容)。

    • 方法:

    ◦ countVK(String s):计算给定字符串中不重叠VK的出现次数。

    ◦ calculateMaxVK():通过改变至多一个字符,计算VK的最大出现次数。

  2. 主类Main:处理输入输出,创建优化器对象并调用方法。

  3. 算法细节:
    • 遍历字符串时跳过已匹配的字符,避免重叠计数。

    • 改变字符后恢复原状,确保每次尝试独立。

测试样例验证

• 输入 #1:2 VK → 输出 1(原始已有VK)

• 输入 #2:2 VV → 输出 1(改变第二个V为K,得到VK)

• 输入 #3:1 V → 输出 0(长度不足)

• 输入 #4:20 VKKKKKKKKKVVVVVVVVVK → 输出 3

• 输入 #5:4 KVKV → 输出 1

该方法通过面向对象封装,清晰且易于扩展,时间复杂度为O(n²),在题目约束下高效可行。

面向对象设计特点总结:

  1. 封装性:每个类都将数据和操作封装在一起
  2. 单一职责:每个类负责一个明确的职责
  3. 方法封装:将逻辑操作封装为类的方法
  4. Comparable接口:在需要排序的题目中实现标准接口
  5. 数据隐藏:使用private修饰属性,提供getter方法
  6. 静态初始化块:在WordToNumber类中使用static块初始化映射表
  7. 构造方法:每个类都有适当的构造方法来初始化对象状态

这些题解既解决了算法问题,又体现了面向对象的设计思想,适合作为OOP入门练习。每道题都展示了不同的面向对象编程技巧,从简单的Calculator类到复杂的映射处理类,涵盖了OOP的基础概念。

posted @ 2026-02-10 01:17  kkman2000  阅读(57)  评论(0)    收藏  举报