챕터8. 실습 문제

2025. 3. 28. 08:58Programming Languages/C++

8.6 실습 문제

문제 1: 학생 성적 관리 시스템

STL 컨테이너와 알고리즘을 사용하여 학생 성적을 관리하는 프로그램을 작성하세요:

#include <iostream>
#include <vector>
#include <map>
#include <string>
#include <algorithm>
#include <numeric>
#include <iomanip>

// 학생 정보를 담는 구조체
struct Student {
    std::string name;
    std::string id;
    std::vector<int> scores;  // 여러 과목 점수
    
    // 평균 점수 계산
    double getAverage() const {
        if (scores.empty()) return 0.0;
        return static_cast<double>(std::accumulate(scores.begin(), scores.end(), 0)) / scores.size();
    }
    
    // 총점 계산
    int getTotal() const {
        return std::accumulate(scores.begin(), scores.end(), 0);
    }
};

// 학생 정보 출력 함수
void printStudent(const Student& student) {
    std::cout << "이름: " << student.name << ", 학번: " << student.id << std::endl;
    std::cout << "점수: ";
    for (const auto& score : student.scores) {
        std::cout << score << " ";
    }
    std::cout << std::endl;
    std::cout << "평균: " << std::fixed << std::setprecision(2) << student.getAverage() << std::endl;
    std::cout << "총점: " << student.getTotal() << std::endl;
    std::cout << "-------------------------" << std::endl;
}

class StudentManager {
private:
    std::vector<Student> students;
    std::map<std::string, size_t> studentMap;  // 학번 -> 인덱스 매핑
    
public:
    // 학생 추가
    void addStudent(const Student& student) {
        // 이미 존재하는 학번인지 확인
        if (studentMap.find(student.id) != studentMap.end()) {
            std::cout << "오류: 이미 존재하는 학번입니다." << std::endl;
            return;
        }
        
        studentMap[student.id] = students.size();
        students.push_back(student);
        std::cout << student.name << " 학생이 추가되었습니다." << std::endl;
    }
    
    // 학생 검색 (학번으로)
    bool findStudent(const std::string& id, Student& outStudent) const {
        auto it = studentMap.find(id);
        if (it != studentMap.end()) {
            outStudent = students[it->second];
            return true;
        }
        return false;
    }
    
    // 학생 정보 갱신
    bool updateStudent(const std::string& id, const Student& newInfo) {
        auto it = studentMap.find(id);
        if (it != studentMap.end()) {
            // 학번은 변경하지 않음
            students[it->second].name = newInfo.name;
            students[it->second].scores = newInfo.scores;
            return true;
        }
        return false;
    }
    
    // 학생 삭제
    bool removeStudent(const std::string& id) {
        auto it = studentMap.find(id);
        if (it != studentMap.end()) {
            size_t index = it->second;
            std::string name = students[index].name;
            
            // 학생 제거 및 맵 갱신
            students.erase(students.begin() + index);
            studentMap.erase(id);
            
            // 맵의 인덱스 업데이트 (삭제된 이후의 학생들)
            for (auto& pair : studentMap) {
                if (pair.second > index) {
                    pair.second--;
                }
            }
            
            std::cout << name << " 학생이 삭제되었습니다." << std::endl;
            return true;
        }
        return false;
    }
    
    // 모든 학생 출력
    void printAllStudents() const {
        if (students.empty()) {
            std::cout << "등록된 학생이 없습니다." << std::endl;
            return;
        }
        
        std::cout << "학생 목록 (" << students.size() << "명):" << std::endl;
        std::cout << "==========================" << std::endl;
        for (const auto& student : students) {
            printStudent(student);
        }
    }
    
    // 성적순으로 정렬하여 출력
    void printStudentsByGrade() const {
        if (students.empty()) {
            std::cout << "등록된 학생이 없습니다." << std::endl;
            return;
        }
        
        // 학생 복사 후 정렬
        std::vector<Student> sortedStudents = students;
        std::sort(sortedStudents.begin(), sortedStudents.end(),
                 [](const Student& a, const Student& b) {
                     return a.getAverage() > b.getAverage();  // 내림차순 정렬
                 });
        
        std::cout << "성적순 학생 목록 (" << sortedStudents.size() << "명):" << std::endl;
        std::cout << "==========================" << std::endl;
        for (size_t i = 0; i < sortedStudents.size(); i++) {
            std::cout << (i + 1) << "등: ";
            printStudent(sortedStudents[i]);
        }
    }
    
    // 특정 점수 이상인 학생 출력
    void printStudentsAboveScore(int minScore) const {
        auto isAboveScore = [minScore](const Student& s) {
            return s.getAverage() >= minScore;
        };
        
        // 조건을 만족하는 학생 수 세기
        int count = std::count_if(students.begin(), students.end(), isAboveScore);
        
        if (count == 0) {
            std::cout << minScore << "점 이상인 학생이 없습니다." << std::endl;
            return;
        }
        
        std::cout << minScore << "점 이상인 학생 목록 (" << count << "명):" << std::endl;
        std::cout << "==========================" << std::endl;
        
        // 조건을 만족하는 학생 출력
        for (const auto& student : students) {
            if (isAboveScore(student)) {
                printStudent(student);
            }
        }
    }
    
    // 통계 출력
    void printStatistics() const {
        if (students.empty()) {
            std::cout << "등록된 학생이 없습니다." << std::endl;
            return;
        }
        
        // 모든 학생의 평균 점수 계산
        std::vector<double> averages;
        for (const auto& student : students) {
            averages.push_back(student.getAverage());
        }
        
        // 전체 평균
        double totalAverage = std::accumulate(averages.begin(), averages.end(), 0.0) / averages.size();
        
        // 최고/최저 평균
        auto minmax = std::minmax_element(averages.begin(), averages.end());
        double minAverage = *minmax.first;
        double maxAverage = *minmax.second;
        
        // 최고/최저 학생 찾기
        auto maxStudent = std::max_element(students.begin(), students.end(),
                                          [](const Student& a, const Student& b) {
                                              return a.getAverage() < b.getAverage();
                                          });
        
        auto minStudent = std::min_element(students.begin(), students.end(),
                                          [](const Student& a, const Student& b) {
                                              return a.getAverage() < b.getAverage();
                                          });
        
        // 통계 출력
        std::cout << "학생 통계:" << std::endl;
        std::cout << "==========================" << std::endl;
        std::cout << "전체 학생 수: " << students.size() << "명" << std::endl;
        std::cout << "전체 평균: " << std::fixed << std::setprecision(2) << totalAverage << std::endl;
        std::cout << "최고 평균: " << maxAverage << " (" << maxStudent->name << ", " << maxStudent->id << ")" << std::endl;
        std::cout << "최저 평균: " << minAverage << " (" << minStudent->name << ", " << minStudent->id << ")" << std::endl;
    }
};

int main() {
    StudentManager manager;
    
    // 샘플 학생 데이터 추가
    Student s1 = {"홍길동", "S001", {85, 90, 78, 92, 88}};
    Student s2 = {"김철수", "S002", {76, 82, 95, 89, 90}};
    Student s3 = {"이영희", "S003", {92, 96, 91, 95, 89}};
    Student s4 = {"박민수", "S004", {68, 72, 78, 65, 70}};
    Student s5 = {"정지원", "S005", {88, 85, 92, 97, 91}};
    
    manager.addStudent(s1);
    manager.addStudent(s2);
    manager.addStudent(s3);
    manager.addStudent(s4);
    manager.addStudent(s5);
    
    int choice;
    bool running = true;
    
    while (running) {
        std::cout << "\n학생 성적 관리 시스템" << std::endl;
        std::cout << "1. 학생 목록 출력" << std::endl;
        std::cout << "2. 성적순 학생 목록 출력" << std::endl;
        std::cout << "3. 학생 검색" << std::endl;
        std::cout << "4. 학생 추가" << std::endl;
        std::cout << "5. 학생 정보 수정" << std::endl;
        std::cout << "6. 학생 삭제" << std::endl;
        std::cout << "7. 특정 점수 이상 학생 출력" << std::endl;
        std::cout << "8. 학생 통계 보기" << std::endl;
        std::cout << "0. 종료" << std::endl;
        std::cout << "선택: ";
        std::cin >> choice;
        
        switch (choice) {
            case 1:
                manager.printAllStudents();
                break;
                
            case 2:
                manager.printStudentsByGrade();
                break;
                
            case 3: {
                std::string id;
                std::cout << "검색할 학번: ";
                std::cin >> id;
                
                Student found;
                if (manager.findStudent(id, found)) {
                    std::cout << "학생 정보:" << std::endl;
                    std::cout << "==========================" << std::endl;
                    printStudent(found);
                } else {
                    std::cout << "학번 " << id << "인 학생을 찾을 수 없습니다." << std::endl;
                }
                break;
            }
                
            case 4: {
                Student newStudent;
                std::cout << "이름: ";
                std::cin >> newStudent.name;
                std::cout << "학번: ";
                std::cin >> newStudent.id;
                
                int numScores;
                std::cout << "과목 수: ";
                std::cin >> numScores;
                
                newStudent.scores.resize(numScores);
                for (int i = 0; i < numScores; i++) {
                    std::cout << "과목 " << (i + 1) << " 점수: ";
                    std::cin >> newStudent.scores[i];
                }
                
                manager.addStudent(newStudent);
                break;
            }
                
            case 5: {
                std::string id;
                std::cout << "수정할 학생의 학번: ";
                std::cin >> id;
                
                Student found;
                if (manager.findStudent(id, found)) {
                    std::cout << "현재 정보:" << std::endl;
                    printStudent(found);
                    
                    std::cout << "새 이름 (현재: " << found.name << "): ";
                    std::cin >> found.name;
                    
                    int numScores;
                    std::cout << "새 과목 수 (현재: " << found.scores.size() << "): ";
                    std::cin >> numScores;
                    
                    found.scores.resize(numScores);
                    for (int i = 0; i < numScores; i++) {
                        std::cout << "과목 " << (i + 1) << " 점수: ";
                        std::cin >> found.scores[i];
                    }
                    
                    if (manager.updateStudent(id, found)) {
                        std::cout << "학생 정보가 업데이트되었습니다." << std::endl;
                    } else {
                        std::cout << "업데이트 실패!" << std::endl;
                    }
                } else {
                    std::cout << "학번 " << id << "인 학생을 찾을 수 없습니다." << std::endl;
                }
                break;
            }
                
            case 6: {
                std::string id;
                std::cout << "삭제할 학생의 학번: ";
                std::cin >> id;
                
                if (!manager.removeStudent(id)) {
                    std::cout << "학번 " << id << "인 학생을 찾을 수 없습니다." << std::endl;
                }
                break;
            }
                
            case 7: {
                int minScore;
                std::cout << "최소 점수: ";
                std::cin >> minScore;
                
                manager.printStudentsAboveScore(minScore);
                break;
            }
                
            case 8:
                manager.printStatistics();
                break;
                
            case 0:
                running = false;
                std::cout << "프로그램을 종료합니다." << std::endl;
                break;
                
            default:
                std::cout << "잘못된 선택입니다. 다시 시도하세요." << std::endl;
        }
    }
    
    return 0;
}

문제 2: 문자열 처리 유틸리티

STL 알고리즘과 컨테이너를 사용한 문자열 처리 유틸리티를 구현하세요:

#include <iostream>
#include <string>
#include <vector>
#include <map>
#include <set>
#include <algorithm>
#include <sstream>
#include <cctype>
#include <iomanip>

class StringUtils {
public:
    // 문자열 분할
    static std::vector<std::string> split(const std::string& str, char delimiter = ' ') {
        std::vector<std::string> tokens;
        std::stringstream ss(str);
        std::string token;
        
        while (std::getline(ss, token, delimiter)) {
            if (!token.empty()) {  // 빈 토큰 제외
                tokens.push_back(token);
            }
        }
        
        return tokens;
    }
    
    // 문자열 합치기
    static std::string join(const std::vector<std::string>& tokens, const std::string& delimiter = " ") {
        if (tokens.empty()) return "";
        
        std::string result = tokens[0];
        for (size_t i = 1; i < tokens.size(); i++) {
            result += delimiter + tokens[i];
        }
        
        return result;
    }
    
    // 대소문자 변환
    static std::string toUpper(const std::string& str) {
        std::string result = str;
        std::transform(result.begin(), result.end(), result.begin(), 
                      [](unsigned char c) { return std::toupper(c); });
        return result;
    }
    
    static std::string toLower(const std::string& str) {
        std::string result = str;
        std::transform(result.begin(), result.end(), result.begin(), 
                      [](unsigned char c) { return std::tolower(c); });
        return result;
    }
    
    // 단어 빈도 계산
    static std::map<std::string, int> wordFrequency(const std::string& text) {
        std::vector<std::string> words = split(text);
        std::map<std::string, int> freq;
        
        for (const auto& word : words) {
            // 구두점 제거 및 소문자 변환
            std::string cleanWord;
            std::copy_if(word.begin(), word.end(), std::back_inserter(cleanWord),
                        [](char c) { return std::isalnum(c); });
            
            if (!cleanWord.empty()) {
                cleanWord = toLower(cleanWord);
                freq[cleanWord]++;
            }
        }
        
        return freq;
    }
    
    // 문자열 앞뒤 공백 제거
    static std::string trim(const std::string& str) {
        auto start = std::find_if_not(str.begin(), str.end(),
                                     [](unsigned char c) { return std::isspace(c); });
        
        auto end = std::find_if_not(str.rbegin(), str.rend(),
                                   [](unsigned char c) { return std::isspace(c); }).base();
        
        return (start < end) ? std::string(start, end) : "";
    }
    
    // 부분 문자열 교체
    static std::string replace(const std::string& str, const std::string& from, const std::string& to) {
        std::string result = str;
        size_t pos = 0;
        
        while ((pos = result.find(from, pos)) != std::string::npos) {
            result.replace(pos, from.length(), to);
            pos += to.length();
        }
        
        return result;
    }
    
    // 회문(palindrome) 검사
    static bool isPalindrome(const std::string& str) {
        // 알파벳만 추출하고 소문자로 변환
        std::string cleaned;
        std::copy_if(str.begin(), str.end(), std::back_inserter(cleaned),
                    [](char c) { return std::isalpha(c); });
        cleaned = toLower(cleaned);
        
        if (cleaned.empty()) return false;
        
        // 원본과 뒤집은 문자열 비교
        std::string reversed = cleaned;
        std::reverse(reversed.begin(), reversed.end());
        
        return cleaned == reversed;
    }
    
    // 문자열에서 특정 문자 제거
    static std::string removeChars(const std::string& str, const std::string& chars) {
        std::string result = str;
        for (char c : chars) {
            result.erase(std::remove(result.begin(), result.end(), c), result.end());
        }
        return result;
    }
    
    // 문자열에서 고유한 문자 찾기
    static std::set<char> uniqueChars(const std::string& str) {
        std::set<char> result(str.begin(), str.end());
        return result;
    }
    
    // 두 문자열의 애너그램(anagram) 여부 확인
    static bool areAnagrams(const std::string& str1, const std::string& str2) {
        // 대소문자 무시, 공백 및 구두점 제거
        auto cleanString = [](const std::string& s) {
            std::string result;
            std::copy_if(s.begin(), s.end(), std::back_inserter(result),
                        [](char c) { return std::isalpha(c); });
            return toLower(result);
        };
        
        std::string s1 = cleanString(str1);
        std::string s2 = cleanString(str2);
        
        if (s1.length() != s2.length()) return false;
        
        std::sort(s1.begin(), s1.end());
        std::sort(s2.begin(), s2.end());
        
        return s1 == s2;
    }
};

int main() {
    int choice;
    bool running = true;
    
    while (running) {
        std::cout << "\n문자열 처리 유틸리티" << std::endl;
        std::cout << "1. 문자열 분할" << std::endl;
        std::cout << "2. 문자열 합치기" << std::endl;
        std::cout << "3. 대문자/소문자 변환" << std::endl;
        std::cout << "4. 단어 빈도 계산" << std::endl;
        std::cout << "5. 문자열 앞뒤 공백 제거" << std::endl;
        std::cout << "6. 부분 문자열 교체" << std::endl;
        std::cout << "7. 회문(Palindrome) 검사" << std::endl;
        std::cout << "8. 특정 문자 제거" << std::endl;
        std::cout << "9. 고유 문자 찾기" << std::endl;
        std::cout << "10. 애너그램(Anagram) 검사" << std::endl;
        std::cout << "0. 종료" << std::endl;
        std::cout << "선택: ";
        std::cin >> choice;
        std::cin.ignore();  // 입력 버퍼 비우기
        
        std::string input, input2, delimiter;
        std::vector<std::string> tokens;
        
        switch (choice) {
            case 1:  // 문자열 분할
                std::cout << "문자열 입력: ";
                std::getline(std::cin, input);
                std::cout << "구분자 입력 (단일 문자): ";
                std::cin >> delimiter;
                
                tokens = StringUtils::split(input, delimiter[0]);
                std::cout << "분할 결과 (" << tokens.size() << "개):" << std::endl;
                for (size_t i = 0; i < tokens.size(); i++) {
                    std::cout << i + 1 << ": \"" << tokens[i] << "\"" << std::endl;
                }
                break;
                
            case 2:  // 문자열 합치기
                std::cout << "합칠 문자열 개수: ";
                int count;
                std::cin >> count;
                std::cin.ignore();
                
                tokens.clear();
                for (int i = 0; i < count; i++) {
                    std::cout << "문자열 " << i + 1 << ": ";
                    std::getline(std::cin, input);
                    tokens.push_back(input);
                }
                
                std::cout << "구분자 입력: ";
                std::getline(std::cin, delimiter);
                
                std::cout << "합친 결과: \"" << StringUtils::join(tokens, delimiter) << "\"" << std::endl;
                break;
                
            case 3:  // 대문자/소문자 변환
                std::cout << "문자열 입력: ";
                std::getline(std::cin, input);
                
                std::cout << "대문자 변환: \"" << StringUtils::toUpper(input) << "\"" << std::endl;
                std::cout << "소문자 변환: \"" << StringUtils::toLower(input) << "\"" << std::endl;
                break;
                
            case 4:  // 단어 빈도 계산
                std::cout << "텍스트 입력: ";
                std::getline(std::cin, input);
                
                std::map<std::string, int> freq = StringUtils::wordFrequency(input);
                std::cout << "단어 빈도 (총 " << freq.size() << "개):" << std::endl;
                for (const auto& pair : freq) {
                    std::cout << std::setw(15) << std::left << pair.first << ": " 
                              << pair.second << "회" << std::endl;
                }
                break;
                
            case 5:  // 문자열 앞뒤 공백 제거
                std::cout << "문자열 입력: ";
                std::getline(std::cin, input);
                
                std::cout << "원본: \"" << input << "\"" << std::endl;
                std::cout << "공백 제거 후: \"" << StringUtils::trim(input) << "\"" << std::endl;
                break;
                
            case 6:  // 부분 문자열 교체
                std::cout << "원본 문자열: ";
                std::getline(std::cin, input);
                std::cout << "찾을 문자열: ";
                std::getline(std::cin, input2);
                std::cout << "바꿀 문자열: ";
                std::getline(std::cin, delimiter);
                
                std::cout << "교체 결과: \"" << StringUtils::replace(input, input2, delimiter) << "\"" << std::endl;
                break;
                
            case 7:  // 회문 검사
                std::cout << "검사할 문자열: ";
                std::getline(std::cin, input);
                
                if (StringUtils::isPalindrome(input)) {
                    std::cout << "\"" << input << "\"은(는) 회문입니다." << std::endl;
                } else {
                    std::cout << "\"" << input << "\"은(는) 회문이 아닙니다." << std::endl;
                }
                break;
                
            case 8:  // 특정 문자 제거
                std::cout << "원본 문자열: ";
                std::getline(std::cin, input);
                std::cout << "제거할 문자들: ";
                std::getline(std::cin, input2);
                
                std::cout << "제거 결과: \"" << StringUtils::removeChars(input, input2) << "\"" << std::endl;
                break;
                
            case 9:  // 고유 문자 찾기
                std::cout << "문자열 입력: ";
                std::getline(std::cin, input);
                
                std::set<char> uniqueChars = StringUtils::uniqueChars(input);
                std::cout << "고유 문자 (" << uniqueChars.size() << "개): ";
                for (char c : uniqueChars) {
                    if (c == ' ') {
                        std::cout << "' ' ";
                    } else {
                        std::cout << c << " ";
                    }
                }
                std::cout << std::endl;
                break;
                
            case 10:  // 애너그램 검사
                std::cout << "첫 번째 문자열: ";
                std::getline(std::cin, input);
                std::cout << "두 번째 문자열: ";
                std::getline(std::cin, input2);
                
                if (StringUtils::areAnagrams(input, input2)) {
                    std::cout << "두 문자열은 애너그램입니다." << std::endl;
                } else {
                    std::cout << "두 문자열은 애너그램이 아닙니다." << std::endl;
                }
                break;
                
            case 0:
                running = false;
                std::cout << "프로그램을 종료합니다." << std::endl;
                break;
                
            default:
                std::cout << "잘못된 선택입니다. 다시 시도하세요." << std::endl;
        }
    }
    
    return 0;
}

'Programming Languages > C++' 카테고리의 다른 글

파일 입출력  (0) 2025.03.28
고급 주제  (0) 2025.03.28
함수 객체와 람다 표현식  (0) 2025.03.28
STL 알고리즘  (0) 2025.03.28
STL 반복자  (0) 2025.03.28