자료구조와 알고리즘/문제풀이

[Baekjoon] 단계별로 풀어보기 : 심화 1

순정법사 2023.08.24

 

이번 알고리즘 문제 풀이는 C로 진행했습니다 

코드에 오탈자나 문제가 있으면 언제든지 댓글로 알려주세요!

 

 

 

 


 

 

 

A. 심화 1

 

심화 1 단계

두세 문자가 한 글자로 묶일 수 있을 때 글자의 수를 세는 문제

www.acmicpc.net

 

1. 새싹

a. 문제지  : 25083

 

b. 정답

📚 풀이

#include <stdio.h>

int main(){
    printf("         ,r'\"7\n");
    printf("r`-_   ,'  ,/\n");
    printf(" \\. \". L_r'\n");
    printf("   `~\\/\n");
    printf("      |\n");
    printf("      |\n");
    
    return 0;
}

 

 

2. 킹, 퀸, 룩, 비숍, 나이트, 폰

a. 문제지  : 10871

 

b. 정답

📚 풀이

#include <stdio.h>

int main(){
    int num;
    char chess[6] = {1,1,2,2,2,8};
    
    for(int i=0; i<6; i++){
        scanf("%d", &num);
        printf("%d ", chess[i] - num);
    }
    return 0;
}

 

 

3. 별 찍기 - 7

a. 문제지  : 2444

 

b. 알고리즘 / 풀이

별찍기 문제를 많이 했지만, 매번 다른 패턴에 실수가 있어서 ㅠㅠ 이번에는 내 방식대로 풀이를 해봤다

위와같이 위 아래 대칭일 경우 풀이 방법에서 위, 아래 출력형식을 다르게 하는 방법을 많이 사용하지만

왠지 모르게 머리에 안들어와서 그냥 한번에 출력하는 방식을 사용해서 풀이해봤다 

 

  1. 사용자로부터 N값을 입력받음
  2. 행 출력 : for 반복문을 이용, 반복 범위는 1부터 2*n-1까지
  3. 열 출력 : 현재 행의 별의 개수를 나타내는 star, 별의 증감을 나타내는 sumb 초기화
    a) if 조건문으로 중간행 확인 후 sumb의 값을 변경해줌
    b) 입력받은 숫자보다 1 작은 값 만큼 공백 출력
    c) 출력 후 조건에 따른 별 찍기
    d) 출력 후 별 조건 변경 후 줄바꿈
  4. 모든 행 출력이 끝나면 종료

 

 

c. 정답

📚 풀이 1 : 별의 개수를 변수화 해 출력하는 방법

#include <stdio.h>
#include <stdlib.h> // abs 함수를 사용하기 위해 포함

int main(){
    int N, star = 1, sumb = 2;
    scanf("%d", &N); // 사용자로부터 N 값을 입력 받음
    
    // 행 출력
    for(int i = 1; i <= 2 * N - 1; i++){ // 1부터 2 * N - 1까지 반복하는 반복문
        // 열 출력
        // 알고리즘
        // 1) 입력받은 숫자보다 1 작은 값만큼 공백 출력
        // 2) 출력 후 그 전 출력한 값 기억(없다면 1)해 2 추가해 별 찍기
        // 3) i > N 이라면 위 알고리즘 반대로 행동
        if(i == N){ // i가 N과 같다면 아래에서 별의 개수가 줄어드는 부분이므로 방향을 바꿈
            sumb = -2; // sumb 값을 -2로 변경하여 별의 개수를 줄이는 방향으로 전환
        } else {
            for(int j = 0; j < abs(N - i); j++){ // 입력받은 숫자보다 1 작은 값만큼 공백 출력
                printf(" ");
            }
        }

        if(star == 1){ // 처음에 별을 한 개만 출력할 때
            printf("*");
        } else {
            for(int k = 0; k < star; k++){ // 별 출력
                printf("*");
            }   
        }
        
        star += sumb; // 다음 행의 별 개수를 계산하기 위해 star 변수 갱신
        printf("\n"); // 한 행 출력이 끝났으므로 줄바꿈
    }
    
    return 0;
}

 

📚 풀이 2 : 위 아래 구분지어 출력하는 방법

#include <stdio.h>
#include <stdlib.h>

int main() {
    int N;
    scanf("%d", &N); // 사용자로부터 N 값을 입력 받음

    // 위쪽 영역 출력
    for (int i = 1; i <= N; i++) { // 위쪽 영역은 1부터 N까지 반복하는 반복문
        // 공백 출력 부분
        for (int j = 1; j <= N - i; j++) { // 입력값 N보다 작은 값만큼 공백을 출력하여 오른쪽 정렬하도록 줄맞춤
            printf(" ");
        }

        // 별 출력 부분
        for (int j = 1; j <= 2 * i - 1; j++) { // 현재 행에서 출력해야 할 별의 개수는 (2 * i - 1)
            printf("*");
        }

        printf("\n"); // 한 행 출력이 끝났으므로 줄바꿈
    }

    // 아래쪽 영역 출력
    for (int i = N - 1; i >= 1; i--) { // 아래쪽 영역은 N - 1부터 1까지 반복하는 반복문
        // 공백 출력 부분
        for (int j = 1; j <= N - i; j++) { // 입력값 N보다 작은 값만큼 공백을 출력하여 오른쪽 정렬하도록 줄맞춤
            printf(" ");
        }

        // 별 출력 부분
        for (int j = 1; j <= 2 * i - 1; j++) { // 현재 행에서 출력해야 할 별의 개수는 (2 * i - 1)
            printf("*");
        }

        printf("\n"); // 한 행 출력이 끝났으므로 줄바꿈
    }

    return 0;
}

 

 

4.  팰린드롬인지 확인하기

a. 문제지  : 2562

 

b. 정답

📚 풀이 1

#include <stdio.h>
#include <string.h>

int main() {
    char word[101]; // 최대 100개의 문자로 이루어진 문자열을 담을 배열
    scanf("%s", word); // 사용자로부터 문자열 입력 받음
    int len = strlen(word); // 입력된 문자열의 길이를 계산

    for (int i = 0; i <= len / 2; i++) { // 문자열의 반 이하만큼 반복
        if (word[i] == word[len - 1 - i]) { // 대칭 위치에 있는 문자를 비교하여 같으면
            if (i == len / 2) { // 회문을 확인하는 반복문이 끝까지 실행되면 회문임을 의미
                printf("1\n"); // 회문일 경우 1 출력
            } else {
                // 아무 동작도 하지 않음
                // 대칭 위치에 있는 문자가 같더라도 다음 반복에서 검사해야 할 문자가 있을 수 있으므로 이 부분은 비워둠
            }
        } else { // 대칭 위치에 있는 문자가 다를 경우
            printf("0\n"); // 회문이 아니므로 0 출력
            break; // 반복문 종료
        }
    }

    return 0;
}

 

👉 위 코드를 반대로 생각해보면 문자가 다를 경우에만 반복문을 깨고 탈출하면 됨!

 

📚 풀이 2 : 풀이 1 코드 정리

#include <stdio.h>
#include <string.h>

int main() {
    char word[101]; // 최대 100개의 문자로 이루어진 문자열을 담을 배열
    scanf("%s", word); // 사용자로부터 문자열 입력 받음
    int len = strlen(word); // 입력된 문자열의 길이를 계산
    
    int isPalindrome = 1; // 팰린드롬 여부를 나타내는 변수, 초기값은 1로 설정
    
    for (int i = 0; i < len / 2; i++) {
        if (word[i] != word[len - 1 - i]) { // 대칭 위치에 있는 문자를 비교하여 다를 경우
            isPalindrome = 0; // 팰린드롬이 아님을 표시
            break; // 반복문 종료
        }
    }
    
    printf("%d\n", isPalindrome); // 팰린드롬 여부 출력
    
    return 0;
}

 

 

5. 단어 공부

a. 문제지  : 1157

b. 알고리즘 / 풀이

  1. 알파벳 등장 횟수 세기
    a) 주어진 문자열을 순회하면서 각 문자의 등장 횟수를 카운트하는 배열을 생성 
    b) 배열의 크기는 26 (알파벳 개수)로 설정
    c) 문자열을 순회하면서 각 문자를 대문자로 변환
    d) 해당 문자가 알파벳인 경우 카운트 배열의 인덱스를 계산하여 카운트를 증가
  2. 가장 많이 나타난 알파벳 찾기:
    a) 등장 횟수를 카운트한 배열을 순회하면서 가장 많이 나타난 알파벳을 찾기 
    b) 만약 가장 많이 나타난 알파벳이 여러 개라면 ?로 처리

 

c. 정답

📚 풀이

#include <stdio.h>
#include <ctype.h> // toupper 함수를 사용하기 위해 포함

int main() {
    char word[1000001]; // 최대 1,000,000 길이의 문자열을 담을 배열
    int count[26] = {0}; // 알파벳 개수를 저장할 배열, 초기값은 0
    
    scanf("%s", word); // 문자열 입력 받음

    for (int i = 0; word[i] != '\0'; i++) {    //문자열 끝까지 순회
        char c = toupper(word[i]); // 대문자로 변환
        count[c - 'A']++; // 해당 알파벳 카운트 증가
    }

    int maxCount = 0; // 가장 많은 알파벳의 개수
    char mostFrequent = '?'; // 가장 많이 나타난 알파벳, 초기값은 '?'

    for (int i = 0; i < 26; i++) {
        if (count[i] > maxCount) { // 더 많은 알파벳이 나타났을 경우
            maxCount = count[i];
            mostFrequent = 'A' + i; // 해당 알파벳 설정
        } else if (count[i] == maxCount) { // 같은 개수의 알파벳이 나타난 경우
            mostFrequent = '?';
        }
    }

    printf("%c\n", mostFrequent); // 결과 출력

    return 0;
}

 

 

6. 크로아티아 알파벳

a. 문제지  : 2941

 

b. 알고리즘 / 풀이

방식 1) 크로아티아 알파벳을 표현하는 문자들의 규칙성을 가지고 문제 풀이

 

  1. 최대 100글자의 단어를 입력 받음
  2. 입력된 단어의 길이를 계산하여 length 변수에 저장
  3. 크로아티아 알파벳 개수를 세기 위한 count 변수를 초기화
  4. 문자열을 한 글자씩 순회하며 크로아티아 알파벳을 판별
  5. 각 알파벳의 경우에 따라 조건문을 사용하여 크로아티아 알파벳을 확인하고 처리
  6. 해당 크로아티아 알파벳의 길이만큼 인덱스를 증가시켜 한 문자로 처리
  7. 조건에 해당하지 않으면 한 글자로 처리하고 count를 증가
  8. 순회가 끝나면 count에 저장된 크로아티아 알파벳 개수를 출력

 

방식 2) 크로아티아 알파벳을 문자열로 등록 후 카운트

 

  1. 최대 100글자의 단어를 입력받음
  2. 크로아티아 알파벳을 표현하는 문자열 배열 target을 정의
  3. target 배열의 길이를 계산하여 length 변수에 저장
  4. 입력된 문자열의 길이를 계산하여 strLen 변수에 저장
  5. 크로아티아 알파벳의 개수를 저장할 count 변수를 초기화
  6. 문자열을 한 글자씩 순회하며 크로아티아 알파벳을 찾기
  7. 내부 루프를 사용하여 target 배열의 각 크로아티아 알파벳과 현재 위치의 문자열을 비교
  8. strncmp 함수를 사용하여 현재 위치부터 target[j]의 길이만큼 비교
  9. 만약 현재 위치부터 target[j]와 길이가 같은 문자열이라면 크로아티아 알파벳을 발견한 것으로 판단
  10. count 변수를 증가시키고, 해당 크로아티아 알파벳의 길이만큼 인덱스를 증가
  11. 만약 크로아티아 알파벳을 찾지 못했다면 한 글자로 처리하고 count를 증가
  12. 순회가 끝나면 최종적으로 count에 저장된 크로아티아 알파벳의 개수를 출력

 

c. 정답

📚 풀이 1

#include <stdio.h>
#include <string.h>

int main() {
    char word[101];
    scanf("%s", word); // 최대 100글자의 단어를 입력 받음
    int length = strlen(word); // 입력된 단어의 길이 계산
    int count = 0; // 크로아티아 알파벳 개수를 세는 변수

    for (int i = 0; i < length; i++) {
        if (word[i] == 'c' && (i + 1) < length) {
            if (word[i + 1] == '=' || word[i + 1] == '-') {
                i++; // 'c=' 또는 'c-'를 만나면 한 문자로 처리하고 인덱스 증가
            }
        } else if (word[i] == 'd' && (i + 1) < length) {
            if (word[i + 1] == 'z' && (i + 2) < length && word[i + 2] == '=') {
                i += 2; // 'dz='를 만나면 한 문자로 처리하고 인덱스 증가
            } else if (word[i + 1] == '-') {
                i++; // 'd-'를 만나면 한 문자로 처리하고 인덱스 증가
            }
        } else if ((word[i] == 'l' || word[i] == 'n') && (i + 1) < length) {
            if (word[i + 1] == 'j') {
                i++; // 'lj' 또는 'nj'를 만나면 한 문자로 처리하고 인덱스 증가
            }
        } else if ((word[i] == 's' || word[i] == 'z') && (i + 1) < length) {
            if (word[i + 1] == '=') {
                i++; // 's=' 또는 'z='를 만나면 한 문자로 처리하고 인덱스 증가
            }
        }
        count++; // 각 경우에 해당하지 않으면 한 글자로 처리하고 count 증가
    }

    printf("%d\n", count); // 크로아티아 알파벳 개수 출력

    return 0;
}

 

📚 풀이 2

#include <stdio.h>
#include <string.h>

int main() {
    char str[101];
    scanf("%s", str); // 최대 100글자의 단어 입력 받음
    
    const char* target[8] = {"c=", "c-", "dz=", "d-", "lj", "nj", "s=", "z="};
    int length = sizeof(target) / sizeof(target[0]); // target 배열의 길이 계산
    int strLen = strlen(str); // 입력된 문자열의 길이 계산
    int count = 0; // 크로아티아 알파벳 개수 카운트 변수
    
    for (int i = 0; i < strLen; i++) { // 문자열을 순회하며 크로아티아 알파벳을 찾음
        int found = 0; // 크로아티아 알파벳이 발견되었는지 여부를 나타내는 변수
        for (int j = 0; j < length; j++) {
            // 문자열의 현재 위치부터 target[j] 길이만큼을 비교하여 크로아티아 알파벳 검사
            if (strncmp(str + i, target[j], strlen(target[j])) == 0) {
                count++; // 크로아티아 알파벳 개수 증가
                found = 1; // 크로아티아 알파벳 발견 표시
                i += strlen(target[j]) - 1; // 치환된 문자열 길이만큼 인덱스 증가
                break; // 문자열이 치환되었으면 다음 문자로 이동
            }
        }
        if (!found) {
            count++; // 크로아티아 알파벳이 아니면 한 글자로 처리
        }
    }

    printf("%d\n", count); // 크로아티아 알파벳 개수 출력
    return 0;
}

 

 

7. 그룹 단어 체커

a. 문제지  : 1316

 

b. 알고리즘  / 풀이

일단 문제부터 쉽게 이해해보자면

그룹단어란 단어에 같은 알파벳의 중복된 연속적인 형태는 가능하지만, 연속되지 않은 형태는 불가능하다는 소리!

 

  1. 단어의 개수 N을 입력 
  2. 그룹 단어가 아닌 개수를 나타내는 count 변수를 초기화
  3. N번 반복하면서 각 단어에 대해 다음 과정을 수행
    a) 단어를 입력
    b) 알파벳의 등장 여부를 저장하는 배열 alphabet을 초기화
    c) 단어의 길이를 계산
    d) 알파벳 검사 시 첫 번째 단어를 제외하고 두번 째 부터 만약 전 알파벳이 이번 알파벳과 다르고, 배열이 카운트 되어있다면 카운트 하고 조건문 빠져나감
    e) 나머지 경우는 알파벳 배열 카운트
  4. 모든 단어에 대한 검사가 끝나면 그룹 단어의 개수(N-count)를 출력

 

c. 정답

📚 풀이

#include <stdio.h>
#include <string.h>

int main() {
    int N;
    scanf("%d", &N); // 단어의 개수 입력 받음
    int count = 0; // 그룹 단어 개수 카운트 변수

    for (int i = 0; i < N; i++) {
        char word[101];
        int alphabet[26] = {0}; // 알파벳의 등장 여부를 저장하는 배열

        scanf("%s", word); // 단어 입력 받음
        int len = strlen(word); // 단어의 길이 계산

        for (int j = 0; j < len; j++) {
            if (j == 0 || word[j - 1] != word[j]) { // 첫 글자이거나 이전 글자와 다른 경우
                if (alphabet[word[j] - 'a'] != 0) { // 이미 등장한 알파벳이라면
                    count++; // 그룹 단어가 아님
                    break; // 더 이상 검사할 필요 없음
                }
                alphabet[word[j] - 'a']++; // 알파벳 등장 여부 표시
            }
        }
    }
    
    printf("%d\n", N - count); // 그룹 단어의 개수 출력
    return 0;
}

 

 

8. 너의 평점은

a. 문제지  : 20206

 

b. 풀이 / 알고리즘

항상 for문만 쓰는 것 같아서 while문을 사용하려고 했는데

여기서 continue를 사용한걸 까먹고 i++이 왜 안되나,, 한참 고민했다ㅠㅠ

continue문의 개념을 확실히 집고 갈 수 있었다!

chatgpt는 항상 올바른 답을 제공하는건 아니지만 너무 큰 도움을 준다 ^0^

 

c. 정답

📚 풀이 : for 반복문을 활용

#include <stdio.h>
#include <string.h>

int main() {

    double total_score = 0.0; // 전공평점의 합을 저장하는 변수
    double total_credit = 0.0; // 총 학점을 저장하는 변수
    
    for (int i = 0; i < 20; i++) {
        char subject[51], grade[3]; // 과목명과 등급을 저장할 배열
        double credit; // 학점을 저장하는 변수
        double grade_score = 0.0; // 등급에 따른 과목평점을 저장하는 변수
        
        scanf("%s %lf %s", subject, &credit, grade); // 과목 정보 입력 받음
        
        // 등급에 따른 과목평점 계산
        if (strcmp(grade, "A+") == 0) {
            grade_score = 4.5;
        } else if (strcmp(grade, "A0") == 0) {
            grade_score = 4.0;
        } else if (strcmp(grade, "B+") == 0) {
            grade_score = 3.5;
        } else if (strcmp(grade, "B0") == 0) {
            grade_score = 3.0;
        } else if (strcmp(grade, "C+") == 0) {
            grade_score = 2.5;
        } else if (strcmp(grade, "C0") == 0) {
            grade_score = 2.0;
        } else if (strcmp(grade, "D+") == 0) {
            grade_score = 1.5;
        } else if (strcmp(grade, "D0") == 0) {
            grade_score = 1.0;
        } else if (strcmp(grade, "F") == 0) {
            grade_score = 0.0;
        } else if (strcmp(grade, "P") == 0) {
            continue; // P 등급은 계산에서 제외
        }
        
        total_score += credit * grade_score; // 해당 과목의 학점과 등급을 곱하여 전공평점의 합에 더함
        total_credit += credit; // 해당 과목의 학점을 총 학점에 더함
    }
    
    if (total_credit == 0.0) {
        printf("0.00000\n"); // 학점이 0이면 전공평점도 0.00000으로 출력
    } else {
        double avg_score = total_score / total_credit; // 전공평점의 평균 계산
        printf("%.5lf\n", avg_score); // 전공평점의 평균을 소수 다섯째 자리까지 출력
    }
    
    return 0;
}

 

📚 풀이 : while 반복문을 활용

#include <stdio.h>
#include <string.h>

int main() {
    double total_score = 0.0; // 전공평점의 합을 저장하는 변수
    double total_credit = 0.0; // 총 학점을 저장하는 변수
    int i = 0; // 반복 횟수를 저장하는 변수
    
    while (i < 20) {
        char subject[51], grade[3]; // 과목명과 등급을 저장할 배열
        double credit; // 학점을 저장하는 변수
        double grade_score = 0.0; // 등급에 따른 과목평점을 저장하는 변수
        
        scanf("%s %lf %s", subject, &credit, grade); // 과목 정보 입력 받음
        
        // 등급에 따른 과목평점 계산
        if (strcmp(grade, "A+") == 0) {
            grade_score = 4.5;
        } else if (strcmp(grade, "A0") == 0) {
            grade_score = 4.0;
        } else if (strcmp(grade, "B+") == 0) {
            grade_score = 3.5;
        } else if (strcmp(grade, "B0") == 0) {
            grade_score = 3.0;
        } else if (strcmp(grade, "C+") == 0) {
            grade_score = 2.5;
        } else if (strcmp(grade, "C0") == 0) {
            grade_score = 2.0;
        } else if (strcmp(grade, "D+") == 0) {
            grade_score = 1.5;
        } else if (strcmp(grade, "D0") == 0) {
            grade_score = 1.0;
        } else if (strcmp(grade, "F") == 0) {
            grade_score = 0.0;
        } else if (strcmp(grade, "P") == 0) {
            i++; // P 등급의 경우에도 반복 횟수 증가
            continue; // P 등급은 계산에서 제외
        }
        
        total_score += credit * grade_score; // 해당 과목의 학점과 등급을 곱하여 전공평점의 합에 더함
        total_credit += credit; // 해당 과목의 학점을 총 학점에 더함
        i++; // 반복 횟수 증가
    }
    
    if (total_credit == 0.0) {
        printf("0.00000\n"); // 학점이 0이면 전공평점도 0.00000으로 출력
    } else {
        double avg_score = total_score / total_credit; // 전공평점의 평균 계산
        printf("%.5lf\n", avg_score); // 전공평점의 평균을 소수 다섯째 자리까지 출력
    }
    
    return 0;
}