인하대학교 컴퓨터공학과를 졸업하기 위해서는, 전공평점이 3.3 이상이거나 졸업고사를 통과해야 한다. 그런데 아뿔싸, 치훈이는 깜빡하고 졸업고사를 응시하지 않았다는 사실을 깨달았다!
치훈이의 전공평점을 계산해주는 프로그램을 작성해보자.
전공평점은 전공과목별 (학점 × 과목평점)의 합을 학점의 총합으로 나눈 값이다.
인하대학교 컴퓨터공학과의 등급에 따른 과목평점은 다음 표와 같다.
A+ 4.5
A0 4.0
B+ 3.5
B0 3.0
C+ 2.5
C0 2.0
D+ 1.5
D0 1.0
F 0.0
P/F 과목의 경우 등급이 P또는 F로 표시되는데, 등급이 P인 과목은 계산에서 제외해야 한다.
과연 치훈이는 무사히 졸업할 수 있을까?
20줄에 걸쳐 치훈이가 수강한 전공과목의 과목명, 학점, 등급이 공백으로 구분되어 주어진다.
치훈이의 전공평점을 출력한다.
정답과의 절대오차 또는 상대오차가
(10^{-4}) 이하이면 정답으로 인정한다.
1 ≤ 과목명의 길이 ≤ 50
과목명은 알파벳 대소문자 또는 숫자로만 이루어져 있으며, 띄어쓰기 없이 주어진다. 입력으로 주어지는 모든 과목명은 서로 다르다.
학점은 1.0,2.0,3.0,4.0중 하나이다.
등급은 A+,A0,B+,B0,C+,C0,D+,D0,F,P중 하나이다.
적어도 한 과목은 등급이 P가 아님이 보장된다.
문자를 숫자로 변환하는 것과, 어떤 자료구조에 담을지 생각하면서 풀어야하는 문제이다.
<string.h> 헤더에 있는 strstr함수를 사용하였다.
strstr함수는 문자열 안에 원하는 문자열이 있는지 찾는 함수로
char *strstr(const char *haystack, const char *needle);
이 형태로 사용하고 needle이 haystack안에 있으면 그 포인터를 없으면 NULL을 반환한다.
🌟 이 때 소수를 어떤 자료형으로 받는게 좋을까?
float형과 double형의 차이에 대해 고민해 보았다.
float형과 double형의 차이는 사용하는 비트갯수에서 오는 정밀도차이이다. 간단히 말하면 double형이 약 2배이상의 비트를 사용하고 정밀도도 더 높다.
소수를 표현하는 방법을 따로 정리해두어 아래 링크로 들어가면 된다.
<정수와 실수의 표현 방법>
scanf로 받으면서 바로 변수에 담아 더해버리면서 처리하는 방법도 있으나, 리스트에 담으면 받은 데이터를 저장해놓고 처리할 수 있을 것 같아 리스트를 선택했다.
이차원 배열을 이용해서 저장할 수 도 있으나 구조체를 선언하는 것이 더 직관적이어서 리스트를 선택했다.
typedef struct s_node{
struct s_node *next;
double n_credit;
double n_grade;
}t_node;
구조체는 위와 같이 선언하였다. 과목명은 사용하지 않을거라 저장하지 않았고, 학점과 과목평점을 double형으로 가지고 있는 노드를 만들었다.
main함수에서 list의 head를 선언, head의 뒤 부터 이어붙이는 방식으로 list에 데이터를 저장했다.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct s_node{
struct s_node *next;
double n_credit;
double n_grade;
}t_node;
t_node *make_lst(void)
{
t_node *new;
new = (t_node *)malloc(sizeof(t_node));
if (!new)
return (0);
new->next = NULL;
return (new);
}
// 새 node를 만드는 함수.
//head node를 만들거나 list에 이어붙일 새 노드를 만드는데 쓰일 것이다.
double trans_credit(char *credit)
{
double f_credit;
if (strstr(credit, "A+") != NULL)
f_credit = 4.5;
else if (strstr(credit, "A0") != NULL)
f_credit = 4.0;
else if (strstr(credit, "B+") != NULL)
f_credit = 3.5;
else if (strstr(credit, "B0") != NULL)
f_credit = 3.0;
else if (strstr(credit, "C+") != NULL)
f_credit = 2.5;
else if (strstr(credit, "C0") != NULL)
f_credit = 2.0;
else if (strstr(credit, "D+") != NULL)
f_credit = 1.5;
else if (strstr(credit, "D0") != NULL)
f_credit = 1.0;
else if (strstr(credit, "F") != NULL)
f_credit = 0.0;
else
f_credit = -1.0;
return (f_credit);
}
//학점 변환하는 함수, 학점이 P이면 넘어갈 수 있게 -1.0으로 flag를 준 것이다.
void lst_addbck(t_node **lst, double credit, double grade)
{
t_node *new;
t_node *temp;
temp = *lst;
if (grade == -1)
return ;
new = make_lst();
while (temp->next != NULL)
temp = temp->next;
new->n_credit = credit;
new->n_grade = grade;
temp->next = new;
}
// list의 가장 마지막에 이어붙이는 함수.
//원본 리스트는 그 주소값에 접근해서 값을 바꾸어주어야되기 때문에 이중포인터로 접근해야한다.
int main(void)
{
char subject[50];
char c_grade[2];
double credit, grade;
t_node *lst;
t_node *temp;
double x, y;
x = 0.0;
y = 0.0;
lst = make_lst();
for(int i = 0; i < 20; i++)
{
scanf("%s", subject);
scanf("%lf", &credit);
scanf("%s", c_grade);
grade = trans_credit(c_grade);
lst_addbck(&lst, credit, grade);
}
temp = lst->next;
//head다음 실제로 데이터가 담긴 부분부터 조회하기 위함
while (temp != NULL)
{
x += (temp->n_credit) * (temp->n_grade);
y += temp->n_credit;
temp = temp->next;
}
if (x == 0 || y == 0)
{
printf("0.000000");
return (0);
}
printf("%f\n", x/y);
return (0);
}
0/0을 하면 nan이 나와서 따로 처리했다. 왜 그런지 찾아보자
y의 값을 n_grade의 합계로 잘못 설정해서 처음에 틀렸었다. 숫자가 많을수록 차분하게 문제를 읽어보자