⊙ 학습 목표
코드스니펫
#include <iostream>
#include <map>
#include <vector>
#include <algorithm>
using namespace std;
class OnlineLearningManagementSystem
{
private:
map<int, map<string, int>> score_by_student;
map<string, map<int, int>> score_by_subject;
public:
// 학생 성적 추가
void add_score(int student_id, string subject_name, int score)
{
score_by_student[student_id][subject_name] = score;
score_by_subject[subject_name][student_id] = score;
}
// 학생의 전체 성적 조회
void print_student_score(int student_id)
{
cout << "학생 ID " << student_id << "의 성적:" << endl;
// student_id를 가진 학생이 존재하지 않을 때
if (score_by_student.find(student_id) == score_by_student.end())
{
// 오류 메시지 출력
cout << student_id << " ID를 가진 학생은 존재하지 않습니다." << endl;
cout << endl;
return;
}
// student_id를 가진 학생이 존재할 때
for (auto student : score_by_student[student_id])
{
// 학생의 과목 성적 출력
cout << "- " << student.first << ": " << student.second << "점" << endl;
}
cout << endl;
}
// 전체 학생의 평균 점수 출력
void print_all_student_average()
{
cout << "전체 과목 평균 점수: " << endl;
double score_sum = 0; // 과목의 점수 합
for (auto subject : score_by_subject)
{
score_sum = 0; // 과목의 점수 합 초기화
// 과목의 모든 점수를 더하기
for (auto student : subject.second)
{
score_sum += student.second;
}
// 소수점 둘째자리까지 나타냄
cout << fixed;
cout.precision(2);
// 과목 평균 출력
cout << "- " << subject.first << ": " << score_sum / subject.second.size() << "점" << endl;
}
cout << endl;
}
// 과목별 최고 점수 학생 조회
void print_highest_score(string subject_name)
{
cout << subject_name << " 최고 점수: ";
// 존재하는 과목인지 확인
if (score_by_subject.find(subject_name) == score_by_subject.end())
{
cout << subject_name << " 과목은 존재하지 않습니다." << endl;
cout << endl;
return;
}
// <학생 아이디, 점수>로 벡터 변환
vector<pair<int, int>> score_list_bySub(score_by_subject[subject_name].begin(), score_by_subject[subject_name].end());
// 성적순 벡터 정렬
sort(score_list_bySub.begin(), score_list_bySub.end(), [](const auto& a, const auto& b) {
if (a.second == b.second) // 성적이 같다면
return a.first < b.first; // 아이디를 비교하여 작은 것이 앞으로
else return a.second > b.second;
});
// 최고 점수 출력
cout << score_list_bySub[0].second << "점" << endl;
// 최고 점수 획득 학생들 출력
for (int i = 0; i < score_list_bySub.size(); i++)
{
// 같은 점수의 학생들을 모두 출력
if (score_list_bySub[0].second == score_list_bySub[i].second)
{
cout << "- 학생 ID: " << score_list_bySub[i].first << endl;
}
else // 점수가 다른 학생이 나왔을 때
{
break; // 반복 중단
}
}
cout << endl;
}
// 성적 구간 검색
void print_score_in_range(string subject_name, int start, int end)
{
cout << subject_name << " 점수 구간 조회(" << start << "~" << end << ")" << endl;
// 존재하는 과목인지 확인
if (score_by_subject.find(subject_name) == score_by_subject.end())
{
cout << subject_name << " 과목은 존재하지 않습니다." << endl;
cout << endl;
return;
}
for (auto subject : score_by_subject[subject_name])
{
// 점수가 구간 안에 들어온다면
if (subject.second >= start && subject.second <= end)
{
// 아이디와 점수 출력
cout << "- 학생 ID: " << subject.first << "(" << subject.second << ")" << endl;
}
}
cout << endl;
}
// 과목별 성적 통계 기능
void statistics_bySub(string subject_name)
{
cout << subject_name << " 성적 통계" << endl;
// 존재하는 과목인지 확인
if (score_by_subject.find(subject_name) == score_by_subject.end())
{
cout << subject_name << " 과목은 존재하지 않습니다." << endl;
cout << endl;
return;
}
// <학생 아이디, 점수>로 벡터 변환
vector<pair<int, int>> score_list_bySub(score_by_subject[subject_name].begin(), score_by_subject[subject_name].end());
// 성적순 벡터 정렬
sort(score_list_bySub.begin(), score_list_bySub.end(), [](const auto& a, const auto& b) {
if (a.second == b.second)
return a.first < b.first;
else return a.second > b.second;
});
// 최고 점수
cout << "- 최고 점수: " << score_list_bySub[0].second << "점" << endl;
// 최저 점수
cout << "- 최고 점수: " << score_list_bySub[score_list_bySub.size()-1].second << "점" << endl;
// 평균 점수
double sum = 0;
for (int i = 0; i < score_list_bySub.size(); i++)
{
sum += score_list_bySub[i].second;
}
cout << "- 평균: " << sum / score_list_bySub.size() << "점" << endl;
// 수강 인원
cout << "- 수강인원: " << score_list_bySub.size() << "명" << endl;
cout << endl;
}
};
int main()
{
OnlineLearningManagementSystem online_learning_management_system = OnlineLearningManagementSystem();
int student_id; // 학생 아이디
string subject_name; // 과목 이름
int score; // 점수
for (int i = 0; i < 6; i++)
{
cout << "[학생 성적 추가]" << endl;
cout << "학생 ID : ";
cin >> student_id;
cout << "과목 이름 : ";
cin >> subject_name;
cout << "점수 : ";
cin >> score;
online_learning_management_system.add_score(student_id, subject_name, score);
cout << "학생 추가 완료 (" << student_id << ", " << subject_name << ", " << score << ")" << endl;
}
cout << endl;
cout << "[학생의 전체 성적 조회]" << endl;
cout << "학생 ID : ";
cin >> student_id;
online_learning_management_system.print_student_score(student_id);
cout << endl;
cout << "[전체 학생의 평균 점수 출력]" << endl;
online_learning_management_system.print_all_student_average();
cout << endl;
cout << "[과목별 최고 점수 학생 조회]" << endl;
cout << "과목 이름 : ";
cin >> subject_name;
online_learning_management_system.print_highest_score(subject_name);
cout << endl;
cout << "[성적 구간 검색]" << endl;
cout << "과목 이름 : ";
cin >> subject_name;
int up, down;
cout << "이상 : ";
cin >> up;
cout << "이하 : ";
cin >> down;
online_learning_management_system.print_score_in_range(subject_name, up, down);
cout << endl;
cout << "[과목별 성적 통계]" << endl;
cout << "과목 이름 : ";
cin >> subject_name;
online_learning_management_system.statistics_bySub(subject_name);
}
구현할 기능
어떤 컨테이너에 데이터를 담아 사용할지 결정하기 전에 먼저 어떤 기능을 구현해야 하는지 살펴봅시다. 어떤 기능을 만들어야 하는지에 따라서 사용해야하는 컨테이너가 달라질 것입니다.
아래는 과제에서 제시된 구현해야할 기능들입니다.
- 학생 성적 추가 기능
- 학생의 전체 성적 조회 기능
- 전체 학생의 과목별 평균 점수 출력 기능
- 과목별 최고 점수 학생 조회 기능
- 특정 과목 성적 구간 검색 기능
- 과목별 성적 통계 기능
모든 기능들을 훑어보았을 때 컨테이너 결정이 쉽지 않을 것임을 느꼈습니다. 보자마자 저는 이렇게 기능을 나누어봤습니다.
학생별, 특정 학생 | 과목별, 특정 과목 |
학생의 성적 추가 기능 학생의 전체 성적 조회 기능 |
전체 학생의 과목별 평균 점수 출력 기능 과목별 최고 점수 학생 조회 기능 특정 과목 성적 구간 검색 기능 과목별 성적 통계 기능 |
어떤 기능은 학생별, 특정 학생에 대해 기능을 구현하기를 원하지만 어떤 기능들은 과목별, 특정 과목의 기능을 구현하기를 원하고 있습니다.
저는 이 때 이게 하나의 컨테이너만으로 데이터를 다룰 수 있는 것이 아니겠구나를 생각했습니다. 일단 그렇다 생각하고 어떤 컨테이너를 사용해야하는지 생각해봅시다.
컨테이너 선택
결론부터 이야기하면 저는 아래 세 가지 컨테이너를 사용하였습니다.
map<int, map<string, int>> score_by_student;
map<string, map<int, int>> score_by_subject;
vector<pair<int, int>> score_list_bySub;
- score_by_student : "학생별", "특정학생"과 관련된 기능을 구현하기 위한 이중 map 컨테이너
- score_by_subject : "과목별", "특정과목"과 관련된 기능을 구현하기 위한 이중 map 컨테이너
- score_list_bySub : 최고, 최저 점수 등을 계산하기 위한 vector 컨테이너
map을 선택한 이유
score_by_student, score_by_subject 둘다 map을 선택한 이유는 두 가지 였습니다.
첫째는 중복이 없어야 한다는 것이었습니다. 같은 학생 ID에 같은 과목의 이름을 입력하면 새로운 데이터가 추가되는 것이 아니라 기존 데이터의 값이 업데이트 되어야 합니다. 따라서 중복이 없이 데이터를 추가할 수 있어야 한다고 생각했습니다.
둘째는 key와 value가 필요한 데이터이기 때문입니다. 학생의 ID, 과목이 이름 등 key가 되는 값들이 존재하고 value는 점수가 되어야 한다고 생각했기 때문에 이중 map을 사용하여 표현하였습니다.
표로 데이터의 모습을 대충 나타내자면 아래와 같습니다.
score_by_student | ||
key(학생 ID) | value(map<string, int>)) | |
key(과목 이름) | value(점수) | |
1001 | "C++" | 85 |
"알고리즘" | 95 | |
1002 | "C++" | 80 |
"알고리즘" | 90 |
score_by_subject | ||
key(과목 이름) | value(map<string, int>)) | |
key(학생 ID) | value(점수) | |
"C++" | 1001 | 85 |
1002 | 80 | |
"알고리즘" | 1001 | 95 |
1002 | 90 |
vector을 선택한 이유
vector를 선택한 이유는 최고 점수, 최소 점수와 같이 순위와 관련된 값들을 계산하기 편할 것 같았기 때문입니다. sort를 사용하면 과제의 조건에 있는 최고 점수, 최소 점수는 물론이고 같은 점수일 때 학생의 ID가 우선순위를 결정하는 것 또한 구현이 가능합니다.
또한 점수만 따로 빼서 사용하고 싶었지만 기능 중 최고 점수와 "최고 점수인 학생의 ID"를 찾아내야 하는 부분이 있어 pair를 사용하게 되었습니다. 점수만 빼서 vector로 만들고 최고 점수를 이용해 최고 점수인 학생의 ID를 다시 역추적 하는 것은 복잡한 구현입니다.
기능 구현(1) : 학생의 성적 추가 기능
map을 사용하였기 때문에 학생 성적 추가 기능은 간단하게 구현할 수 있습니다. 단 학생별로 되어 있는 score_by_student와 과목별로 되어 있는 score_by_subject 모두 같은 데이터를 추가해주어야 한다는 점을 주의해야 합니다.
// 학생 성적 추가
void add_score(int student_id, string subject_name, int score)
{
score_by_student[student_id][subject_name] = score;
score_by_subject[subject_name][student_id] = score;
}
insert를 사용할 수도 있지만 []연산자를 사용하여 점수를 추가했습니다. map에 student_id와 subject_name 두 키가 모두 같다면 새로운 key를 추가하지 않고 있는 key의 score 값만 추가합니다.
기능 구현(2) : 학생의 전체 성적 조회 기능
학생의 전체 성적 조회 기능은 특정 학생의 전체 성적을 조회하는 기능이기 때문에 score_by_student에 저장된 데이터를 사용합니다.
// 학생의 전체 성적 조회
void print_student_score(int student_id)
{
cout << "학생 ID " << student_id << "의 성적:" << endl;
// student_id를 가진 학생이 존재하지 않을 때
if (score_by_student.find(student_id) == score_by_student.end())
{
// 오류 메시지 출력
cout << student_id << " ID를 가진 학생은 존재하지 않습니다." << endl;
cout << endl;
return;
}
// student_id를 가진 학생이 존재할 때
for (auto student : score_by_student[student_id])
{
// 학생의 과목 성적 출력
cout << "- " << student.first << ": " << student.second << "점" << endl;
}
cout << endl;
}
먼저 find를 이용해 매개변수로 들어온 student_id가 존재하는 학생인지부터 확인합니다. 존재하지 않는다면 오류 메시지를 출력하고 리턴하고 끝내도록 합니다.
만약 학생이 존재한다면 해당 학생의 value를 for문으로 돌려 모든 과목의 성적을 출력합니다.
기능 구현(3) : 전체 학생의 과목별 평균 점수 출력 기능
전체 학생의 과목별 평균 점수 출력 기능은 "과목별"이기 때문에 score_by_subject를 이용합니다.
// 전체 학생의 평균 점수 출력
void print_all_student_average()
{
cout << "전체 과목 평균 점수: " << endl;
double score_sum = 0; // 과목의 점수 합
for (auto subject : score_by_subject)
{
score_sum = 0; // 과목의 점수 합 초기화
// 과목의 모든 점수를 더하기
for (auto student : subject.second)
{
score_sum += student.second;
}
// 소수점 둘째자리까지 나타냄
cout << fixed;
cout.precision(2);
// 과목 평균 출력
cout << "- " << subject.first << ": " << score_sum / subject.second.size() << "점" << endl;
}
cout << endl;
}
이중 for문을 돌면서 과목별로 모든 점수를 더하고 과목 수강자의 수를 나누어 평균을 냅니다. 이 때 fixed와 precision을 이용하여 소수점 둘째 자리까지 나타내도록 설정해줍니다.
기능 구현(4) : 과목별 최고 점수 학생 조회 기능
마찬가지로 "과목별" 최고 점수 학생 조회 기능이기 때문에 score_by_subject를 사용합니다. 거기에 순위를 알 수 있어야 해서 score_list_bySub 벡터를 사용합니다.
// 과목별 최고 점수 학생 조회
void print_highest_score(string subject_name)
{
cout << subject_name << " 최고 점수: ";
// 존재하는 과목인지 확인
if (score_by_subject.find(subject_name) == score_by_subject.end())
{
cout << subject_name << " 과목은 존재하지 않습니다." << endl;
cout << endl;
return;
}
// <학생 아이디, 점수>로 벡터 변환
vector<pair<int, int>> score_list_bySub(score_by_subject[subject_name].begin(), score_by_subject[subject_name].end());
// 성적순 벡터 정렬
sort(score_list_bySub.begin(), score_list_bySub.end(), [](const auto& a, const auto& b) {
if (a.second == b.second) // 성적이 같다면
return a.first < b.first; // 아이디를 비교하여 작은 것이 앞으로
else return a.second > b.second;
});
// 최고 점수 출력
cout << score_list_bySub[0].second << "점" << endl;
// 최고 점수 획득 학생들 출력
for (int i = 0; i < score_list_bySub.size(); i++)
{
// 같은 점수의 학생들을 모두 출력
if (score_list_bySub[0].second == score_list_bySub[i].second)
{
cout << "- 학생 ID: " << score_list_bySub[i].first << endl;
}
else // 점수가 다른 학생이 나왔을 때
{
break; // 반복 중단
}
}
cout << endl;
}
먼저 매개변수로 받은 subject_name이 존재하는 과목명인지부터 확인합니다. 과목명이 존재하지 않는다면 에러 메시지를 띄우고 리턴하여 종료합니다.
만약 과목명이 존재한다면 진짜 해야할 일을 시작하면 됩니다. 먼저 <학생 아이디, 점수>로 pair가 들어간 벡터를 만들어줍니다. 그리고 이 벡터에는 score_by_subject중 subject_name의 과목명을 키값으로 가지고 있던 map의 값들을 넣어줍니다.
벡터로 변환한 진짜 목적인 sort를 진행해줍니다. sort는 맨 마지막에 매개변수에 비교 조건을 넣을 수 있다는 것 이미 알고 계실겁니다.
기능 구현(5) : 성적 구간 검색 기능
큰 설명
// 성적 구간 검색
void print_score_in_range(string subject_name, int start, int end)
{
cout << subject_name << " 점수 구간 조회(" << start << "~" << end << ")" << endl;
// 존재하는 과목인지 확인
if (score_by_subject.find(subject_name) == score_by_subject.end())
{
cout << subject_name << " 과목은 존재하지 않습니다." << endl;
cout << endl;
return;
}
for (auto subject : score_by_subject[subject_name])
{
// 점수가 구간 안에 들어온다면
if (subject.second >= start && subject.second <= end)
{
// 아이디와 점수 출력
cout << "- 학생 ID: " << subject.first << "(" << subject.second << ")" << endl;
}
}
cout << endl;
}
작은 제목
작은 설명
기능 구현(6) : 과목별 성적 통계 기능
큰 설명
// 과목별 성적 통계 기능
void statistics_bySub(string subject_name)
{
cout << subject_name << " 성적 통계" << endl;
// 존재하는 과목인지 확인
if (score_by_subject.find(subject_name) == score_by_subject.end())
{
cout << subject_name << " 과목은 존재하지 않습니다." << endl;
cout << endl;
return;
}
// <학생 아이디, 점수>로 벡터 변환
vector<pair<int, int>> score_list_bySub(score_by_subject[subject_name].begin(), score_by_subject[subject_name].end());
// 성적순 벡터 정렬
sort(score_list_bySub.begin(), score_list_bySub.end(), [](const auto& a, const auto& b) {
if (a.second == b.second)
return a.first < b.first;
else return a.second > b.second;
});
// 최고 점수
cout << "- 최고 점수: " << score_list_bySub[0].second << "점" << endl;
// 최저 점수
cout << "- 최고 점수: " << score_list_bySub[score_list_bySub.size()-1].second << "점" << endl;
// 평균 점수
double sum = 0;
for (int i = 0; i < score_list_bySub.size(); i++)
{
sum += score_list_bySub[i].second;
}
cout << "- 평균: " << sum / score_list_bySub.size() << "점" << endl;
// 수강 인원
cout << "- 수강인원: " << score_list_bySub.size() << "명" << endl;
cout << endl;
}
작은 제목
작은 설명
배운 내용 정리
- 내용
'Coding Test' 카테고리의 다른 글
11강 과제 : 블루트포스 최적화 (0) | 2025.03.11 |
---|---|
블루트포스 (0) | 2025.03.11 |
STL 기본 구조 (0) | 2025.02.05 |