Week5-배열과 테크닉

하스코딩·2025년 4월 1일

1. 전화번호 - 카운팅 정렬

풀이

  • 이 문제는 전화번호의 범위가 0~9999로 정해져 있으므로 카운팅 정렬을 사용하면 쉽게 해결할 수 있다.
  • 이렇게 0~9999까지의 int 배열 table을 0으로 초기화해두고, 입력받은 data값 자체를 table 배열의 인덱스로 사용해 카운트(++)해주면 정렬된 빈도수를 구할 수 있다.
  • 그렇게 정렬된 table 배열 전체를 탐색하며, 전화번호 i의 카운트 중 최댓값만 구해주면 된다.
#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
#include <algorithm>
#include <vector>
#include <cmath>
using namespace std;

const int MAX_TABLE_LENGH = 10000;//전화번호 4자 최대 크기

//카운팅 정렬 수행 함수
void fillFrequencyTable(vector<int>& data, vector<int>& table) {

    for (int i = 0; i < data.size(); i++) {
        table[data[i]]++;
    }
}

//가장 많이 등장한 번호 반환 함수(여러개인 경우 사전순 빠른것)
int getFreqeuntMember(vector<int> data) {
    
    vector<int> table(MAX_TABLE_LENGH);
        
    //카운팅 정렬 수행
    fillFrequencyTable(data, table);

    int frequentNum = 0;//최대 카운팅 수 저장 변수

    //카운팅 배열(전화번호 i) 전체를 탐색하며
    for (int i = 1; i < table.size(); i++) {

        //빈도수 더 큰 수 등장 시 최댓값을 갱신
        //단, 같은 경우(<=) 갱신x하므로 사전순 빠른것만 담기게 됨
        if (table[frequentNum] < table[i]) {
            frequentNum = i;
        }
    }
    return frequentNum;

}

int main() {

    //전화번호 수 N입력
    int n;
    cin >> n;

    //N개의 전화번호 뒷자리 4자 입력(0000~9999)
    vector<int> data(n);
    for (int i = 0; i < n; i++) {
        cin >> data[i];
    }

    //빈도수 가장 높은 전화번호 반환
    int result = getFreqeuntMember(data);

    //결과 출력
    cout << result << endl;

    return 0;
}

2. 페인트

풀이

  • 앞서 해결한 문제의 조금 더 복잡한 버전이다.
  • 우선 left, right, color 등을 편하게 관리하기 위해 Painting 클래스를 생성했다.
  • 그리고 m회의 색칠 정보를 입력받은 후 left, right를 -1씩 해줘 0~n-1로 맞춰주었다.

solve 메소드

  • 먼저 전달받은 좌석의 수 n을 사용해 seats 배열을 생성했다.
  • 그리고 m회 색칠하면서 for문 중첩을 통해 left~right 까지 color로 seats 배열에 색깔 정보를 담았다.
  • 이제 0~99번 색깔의 정보를 담은 카운팅 배열 table을 선언하고, 카운팅 정렬을 해준다. 이러면 table은 n개의 seats색을 인덱스로 카운팅하게 된다.
  • 이제 카운트가 0인 색을 제외하고, 전체 table을 탐색한다.
  • max/minColor 색과 i색을 table의 인덱스로 넣어, 카운트가 더 큰/작은 것으로 갱신해 나간다.
  • 이후 이를 두줄로 출력하면 끝이다. 꼭 마지막에 동적할당했던 seats, table 배열을 delete 해줘야 한다.
#include <stdio.h>
#include <iostream>
using namespace std;
//0~99의 색을 사용해 1~n까지의 의자에 페인트를 칠한다.

const int MAX_COLOR_NUMBER = 100;//색깔 수

//좌석 1회 색칠에 대한 정보 클래스
class Painting {
public:
    int left;
    int right;
    int color;

    //기본생성자
    Painting() {}

    Painting(int left, int right, int color) {
        this->left = left;
        this->right = right;
        this->color = color;
    }
};

//카운팅 정렬 수행 함수
void fillFrequencyTable(int data[], int n, int table[]) {

    //n개의 좌석 각각의 색깔을 인덱스로 해
    //0~99의 색깔 배열을 카운팅 
    for (int i = 0; i < n; i++) {
        table[data[i]]++;
    }
}

//가장 많은 좌석이 가진 색, 가장 적은 좌석이 가진 색 번호 출력
//여러개인 경우 작은 번호 출력
int solve(int n, int m, const Painting paint[]) {

    //n개의 좌석 배열 생성, 초기값 0번 색칠
    int* seats = new int[n] {};

    //좌석을 m번 칠함
    for (int i = 0; i < m; i++) {
        //m개의 paint[]마다 left~right까지 색을 color로 칠함
        for (int j = paint[i].left; j <= paint[i].right; j++) {
            seats[j] = paint[i].color;
        }
    }

    //0~99번 색까지의 카운팅 배열 선언(기본값 0)
    int* table = new int[MAX_COLOR_NUMBER] {};
    //카운팅 정렬 수행
    fillFrequencyTable(seats, n, table);

    int minColor = seats[0];//카운팅 최댓값(최대 색 번호)
    int maxColor = seats[0];//카운팅 최소값(최소 색 번호)

    //색번호 0~99번 까지 table 배열을 탐색하며
    for (int i = 0; i < MAX_COLOR_NUMBER; i++) {
        
        //한번도 칠하지 않은 색은 skip해 연산량 감소
        if (table[i] == 0) {
            continue;
        }

        //max/minColor와 i색 중 빈도수 큰/작은 것으로 갱신
        //단, 같은 경우(<=) 갱신x하므로 작은 번호 색만 담기게 됨
        if (table[maxColor] < table[i]) {
            maxColor = i;
        }
        if (table[minColor] > table[seats[i]]) {
            minColor = i;
        }
    }

    cout << maxColor << endl;
    cout << minColor << endl;
    delete[] seats;
    delete[] table;
}

int main() {
    int n, m;
    cin >> n >> m;

    //m회의 색칠정보를 저장할 배열
    Painting* paint = new Painting[m];

    //m회의 페인트 칠할 경우를 입력
    //(left~right번까지 color색으로)
    for (int i = 0; i < m; i++) {
        cin >> paint[i].left >> paint[i].right >> paint[i].color;

        //좌석 번호 1~n입력이므로, -1해서 0~n-1로 맞춰줌
        paint[i].left -= 1;
        paint[i].right -= 1;
    }

    //정답 구함
    solve(n, m, paint);

    return 0;
}

3. 응모 - vector<> 자료구조

풀이

  • 이번 문제는 vector를 사용해서 해결해 볼 것이다. 먼저 벡터의 멤버 함수는 아래와 같다.
vector<int> v(5);// 기본값 0인 5개 원소 배열
vector<int> v(5,2);// 기본값 2인 5개 원소 배열

v.size();// 원소 개수 반환
v.clear();// 모든 원소 제거(메모리는 유지)
v.resize(n,3);// 크기를 n으로 변경하고, 모든 값 3으로 초기화
v.push_back(7);// 마지막 원소 뒤에 7 삽입
  • 벡터를 배열 대신 사용하는 것 말고는 크게 다르지 않다.
  • 단순히 입력받은 n개의 시리얼 번호 data를 인덱스로 넣어 카운팅 정렬을 해준 뒤, table 전체를 탐색하며 1번만 나온 시리얼 번호를 ret 벡터에 넣어 반환하면 된다.
#include <stdio.h>
#include <iostream>
#include <vector>
using namespace std;

const int MAX_SERIAL_NUMBER = 100000;//시리얼 번호 최대값

//카운팅 정렬 수행 함수
void fillFrequencyTable(const vector<int> &data, int n, vector<int> &table) {
    
    table.clear();
    table.resize(MAX_SERIAL_NUMBER + 1, 0);//크기와 기본값 초기화

    //n개의 데이터를 인덱스로 해
    //0~10만의 시리얼 번호 배열을 카운팅 
    for (int i = 0; i < n; i++) {
        table[data[i]]++;
    }
}

//유일한 시리얼 번호만 추려 출력
vector<int> getUniqueElements(const vector<int> &data, int n) {
    
    //유일한 원소들 배열
    vector<int> ret;

    //1~10만 카운팅 배열 선언
    vector<int> table;
    fillFrequencyTable(data, n, table);

    //카운팅 정렬된 table 배열을 탐색하며
    for (int i = 1; i < MAX_SERIAL_NUMBER; i++) {
        //1번 나온 시리얼 번호만
        if (table[i] == 1) {
            ret.push_back(i);//결과 벡터에 삽입
        }
    }
    return ret;
}
    
int main() {
    int n;
    //시리얼 번호 수 n 입력
    cin >> n;

    vector<int> data = vector<int>(n);
    //n개의 시리얼 번호 입력
    for (int i = 0; i < n; i++) {
        cin >> data[i];
    }

    //중복 시리얼 번호 제외, 나머지 번호 공백구분 출력
    const vector<int> result = getUniqueElements(data, n);

    //result 크기만큼 결과 출력
    for (int i = 0; i < result.size(); i++) {
        cout << result[i] << " ";
    }

    return 0;
}

4. 피보나치

풀이

  • 먼저 나중에 계속 사용할 수 있도록 피보나치 벡터를 전역변수로 선언해주고, main 첫줄에서 문제의 최대 크기로 피보나치 수열 벡터 FIBONACCI_TABLE을 생성해 준다.
  • 결과를 반환할 ret 벡터를 최대크기보다 1크게 선언해주고, 공식에 따라 for문으로 ret 벡터에 수열 값을 대입해줬다.
  • 그리고 출력 형식을 보면 아래 8자리만 출력하면 된다고 했으므로, 간단히 ret[i] % 10^8을 해줘서 넣어야 한다.
    " (a % MOD) + (b % MOD) == (a + b) % MOD가 성립하므로 이렇게 매번 해줘도 피보나치 수열에는 지장이 없다.
  • 이제 getFibo 메소드를 호출해 이 벡터에서 n번째 피보나치 수열을 반환받아 출력하면 된다.
#include <stdio.h>
#include <iostream>
#include <vector>
using namespace std;

const int MAX_N = 1000000;//최대 피보나치 크기
vector<int> FIBONACCI_TABLE;//앞으로 계속 쓸 피보나치 벡터 전역변수


//n까지의 피보나치 수열 벡터를 만들어 반환하는 함수
vector<int> makeFibonacciTable(int n) {
    const int MOD = 100000000;//10^8

    //피보나치 배열 ret은 1부터 시작(1~10만)
    vector<int> ret(n + 1);
    ret[1] = 0;//첫 항
    ret[2] = 1;//두번째 항

    //f(n) = f(n-1) + f(n-2)
    for (int i = 3; i <= n; i++) {
        ret[i] = ret[i - 1] + ret[i - 2];
        
        ret[i] %= MOD;//아래 8자리만 추출
;   }
    return ret;
}

//미리 만들어 둔 피보나치 수열 중 n번째 피보나치 수열 반환 함수
int getFibo(int n) {

    int result = FIBONACCI_TABLE[n];

    return result;
}

int main() {
    //피보나치 수열을 최대 크기만큼 미리 만들어 전처리 해둠
    FIBONACCI_TABLE = makeFibonacciTable(MAX_N);

    int t;
    //테스트 케이스 수 t 입력
    cin >> t;

    //t개의 정수 n 입력
    for (int i = 0; i < t; i++) {
        int n;
        cin >> n;

        //n번째 피보나치 수열 리턴
        int result = getFibo(n);
        cout << result << endl;
    }

    FIBONACCI_TABLE.clear();
    return 0;
}

0개의 댓글