https://school.programmers.co.kr/learn/courses/30/lessons/150365

 

프로그래머스

코드 중심의 개발자 채용. 스택 기반의 포지션 매칭. 프로그래머스의 개발자 맞춤형 프로필을 등록하고, 나와 기술 궁합이 잘 맞는 기업들을 매칭 받으세요.

programmers.co.kr

<풀이>

1. 미로 정보를 입력받는다.

2. 사전 순으로 가장 빠른 미로 탈출 경로를 반환한다.

 

<해법>

1. 완전탐색 X

=> 평범한 길찾기 문제라는 생각이 듭니다. 하지만 이 문제는 k가 최대 2500이므로, 완전탐색으로 풀 때 최악의 경우 O(4^2500)의 시간복잡도를 가집니다. 따라서, 완전탐색 알고리즘은 배제해야합니다.

 

2. 불가능한 경우 찾기

=> 먼저 "impossible"한 경우부터 생각해보겠습니다.

첫번째로, 출발지점에서 끝지점까지 갈 수 있는 최단거리보다 k가 작을 경우 불가능합니다. 여기서, 출발지점에서 끝지점까지 최단거리를 계산하는 방법은 매우 간단합니다. 지도 상에 장애물이 없으므로, 맨해튼 거리계산법(ㅣx - rㅣ+ ㅣy - cㅣ)를 이용해서 구할 수 있습니다. 따라서, if(최단거리 > k) answer = "impossible" 입니다.

두번째로, (k - 최단거리) % 2 == 1 일 때 불가능합니다. 이유는 k번을 채우기 위해서는 어디선가 왔다갔다를 해야하기 때문입니다. 단순히 k번을 이용한 거리를 구하라고 한다면, 출발지점에서 끝지점까지 간 후 끝지점에서 바로 옆칸으로 왔다갔다하면 구할 수 있습니다. 그런데 이 때, '왔다갔다'를 해야하므로 2번이 필요합니다. 만약 도착지점에서 k가 1이라면 불가능합니다. 따라서, if((k - 최단거리) % 2 == 1) answer = "impossible" 입니다.

 

3. 사전순으로 가장 빠른 경로 찾기

=> 위 경우를 제외하고서는 k번 이동하여 출발지점부터 끝지점까지 갈 수 있습니다. 이제 사전순으로 가장 빠른 경로를 찾아야합니다. 사전순으로 가장 빠르려면, 앞자리가 가장 빨라야합니다. 이 말은 'd(아래)로 갈 수 있으면, d로 가는게 가장 좋다.' 입니다. 또, 'd -> l -> r -> u 순으로 갈 수있는 곳을 탐색해서 간다.' 가 됩니다. 

그렇다면 이제 해당 방향으로 가도 되는지 안되는지 판단하는 일만 남았습니다. 해당 방향으로 가도 되려면, 먼저 맵을 벗어나면 안됩니다. 그리고 또 한가지는 위에 "impossible"한 경우가 아니면 됩니다. 이 말은 다음 위치에서 최단거리 <= 남은 k 면 된다는 뜻입니다. 이렇게 하면 greedy하게 사전순으로 가장 빠른 방향으로 이동하며 정답을 구할 수 있습니다.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
#include <string>
#include <vector>
 
using namespace std;
 
char d[] = { 'd''l''r''u' };
int dx[] = { 1,0,0,-1 };
int dy[] = { 0,-1,1,0 };
 
bool isInner(int x, int y, int n, int m) {
    if (x <= 0 || y <= 0 || x > n || y > m) return false;
    return true;
}
 
string solution(int n, int m, int x, int y, int r, int c, int k) {
    string answer = "";
 
    int shortest = abs(x - r) + abs(y - c);
    if (shortest > k || (k - shortest) % 2) answer = "impossible";
    else {
        while (k--) {
            for (int i = 0; i < 4; i++) {
                int nx = x + dx[i];
                int ny = y + dy[i];
                if (!isInner(nx, ny, n, m)) continue;
                int nshortest = abs(nx - r) + abs(ny - c);
                if (nshortest > k) continue;
                x = nx, y = ny;
                answer += d[i];
                break;
            }
        }
    }
 
    return answer;
}

 

Greedy 알고리즘에 대해 알아볼 수 있었습니다.

https://school.programmers.co.kr/learn/courses/30/lessons/150366

 

프로그래머스

코드 중심의 개발자 채용. 스택 기반의 포지션 매칭. 프로그래머스의 개발자 맞춤형 프로필을 등록하고, 나와 기술 궁합이 잘 맞는 기업들을 매칭 받으세요.

programmers.co.kr

<풀이>

1. 표를 편집하는 명령어를 입력받는다.

2. 명령어를 실행한 결과를 반환한다.

 

<해법>

1. 알고리즘 선택하기

=> 문제를 읽고 가장 먼저 Union-Find 알고리즘이 떠오릅니다. 아래는 Union-Find 알고리즘에 따라 구현 방법에 대해 정리한 생각입니다.

(1) 'UPDATE r c value'

: 표의 (r, c)의 부모를 찾아 value를 바꾼다.

(2) 'UPDATE value1 value2'

: 표의 모든 value1을 value2로 바꾼다. 표의 크기는 50x50 = 2500이고 명령어의 갯수는 최대 1000개이므로, O(2500x1000)의 시간복잡도로 풀이 가능하다.

(3) 'MERGE r1 c1 r2 c2'

: (r2, c2)의 부모를 (r1, c1)의 부모로 바꾼다. 데이터는 규칙에 따라 바꾼다.

(4) 'PRINT r c'

: (r, c)의 부모를 찾아 value를 출력한다.

(5) 'UNMERGE r c'

: 여기서 막힙니다. 표의 모든 셀과 (r, c)의 부모가 같은지 확인해야하는데, 이 경우 O(2500x2500x1000)의 시간복잡도가 되기 때문입니다(Union-Find의 find함수가 최악의 경우 O(N)이기 때문). 그렇다면 어떻게 해야할까요?

 

2. UNMERGE 해결하기

=> 생각해보면, Union-Find 알고리즘은 Union과 Find에만 관심이 있는 알고리즘입니다. 저희 문제와 같이 UNMERGE하는 부분에는 관심이 없습니다. 따라서, 이 알고리즘의 좋은 부분만 가져와 저희의 문제에 맞게 조금 변형해서 사용해야합니다. UNMERGE 명령어에서 가장 큰 문제점은 셀의 부모가 제 멋대로라는 점입니다.

위 그림처럼 부모를 찾기위해 계속해서 타고들어가야 하기 때문에 3,4,5번이 모두 1번 셀이라는 것을 단번에 알 수 없습니다. 그렇다면 3,4,5번이 모두 2번처럼 1로 표시되면 어떨까요? 정말 간단해집니다. 모든 셀을 돌며 부모가 1인것만 다시 되돌리면 되기 때문입니다. 그리고 이렇게 되면 O(2500x1000)의 시간복잡도가 되므로 풀 수 있습니다. 

 

3. MERGE 교체하기

=> 3,4,5번의 부모가 모두 1로 표현되려면, MERGE부터 바꿔야합니다. 기존 Union-Find의 경우는 부모만 바꾸어 주었다면, 저희는 부모의 자식들 또한 바꾸어주어야합니다.

위 그림처럼 교체하면 MERGE는 O(2500x1000)의 시간복잡도를 가지게 되므로 풀 수 있습니다.

 

4. 구현 전 정리

=> 아래는 구현 전 정리한 생각입니다.

(1) 셀은 1차원 배열로 바꿔서 생각한다. 부모를 정수로 편하게 관리할 수 있기 때문.

(2) 부모 배열과 데이터 배열 두 개를 관리한다.

(3) 빈 데이터는 "EMPTY"를 넣는다. 데이터는 알파벳 소문자와 숫자로만 이루어져있으므로 가능하고, 출력할 때 편리하다.

자세한 구현은 아래 코드를 참고해주세요.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
#include <string>
#include <vector>
#include <sstream>
 
using namespace std;
 
int link[2501];
string value[2501];
 
int flatPos(int r, int c) {
    return (r - 1* 50 + c;
}
 
vector<string> solution(vector<string> commands) {
    vector<string> answer;
 
    //초기화
    for (int i = 1; i <= 2500; i++) {
        link[i] = i;
        value[i] = "EMPTY";
    }
 
    //해법
    for (string &command : commands) {
        vector<string> v;
        stringstream ss(command);
        string token;
        while (ss >> token) v.push_back(token);
 
        if (v[0== "UPDATE") {
            if (v.size() == 4) { //1. UPDATE r c value
                int pos = flatPos(stoi(v[1]), stoi(v[2]));
                string val = v[3];
                int parent = link[pos];
                value[parent] = val;
            }
            else { //2. UPDATE value1 value2
                string val1 = v[1], val2 = v[2];
                for (int i = 1; i <= 2500; i++) {
                    if (value[i] == val1) value[i] = val2;
                }
            }
        }
        else if (v[0== "MERGE") { //3. MERGE r1 c1 r2 c2
            int pos1 = flatPos(stoi(v[1]), stoi(v[2])), pos2 = flatPos(stoi(v[3]), stoi(v[4]));
            int parent1 = link[pos1], parent2 = link[pos2];
            if (parent1 != parent2) {
                for (int i = 1; i <= 2500; i++) {
                    if (link[i] == parent2) link[i] = parent1;
                }
                string val1 = value[parent1], val2 = value[parent2];
                if (val1 == "EMPTY" && val2 != "EMPTY") {
                    value[parent1] = value[parent2];
                }
                else {
                    value[parent2] = value[parent1];
                }
            }
        }
        else if (v[0== "UNMERGE") { //4. UNMERGE r c
            int pos = flatPos(stoi(v[1]), stoi(v[2]));
            int parent = link[pos];
            string val = value[parent];
            for (int i = 1; i <= 2500; i++) {
                if (link[i] == parent) {
                    link[i] = i;
                    value[i] = "EMPTY";
                }
            }
            value[pos] = val;
        }
        else { //5. PRINT r c
            int pos = flatPos(stoi(v[1]), stoi(v[2]));
            int parent = link[pos];
            answer.push_back(value[parent]);
        }
    }
 
    return answer;
}

 

Union-Find에 대해 알아볼 수 있는 문제였습니다.

https://school.programmers.co.kr/learn/courses/30/lessons/150367

 

프로그래머스

코드 중심의 개발자 채용. 스택 기반의 포지션 매칭. 프로그래머스의 개발자 맞춤형 프로필을 등록하고, 나와 기술 궁합이 잘 맞는 기업들을 매칭 받으세요.

programmers.co.kr

<풀이>

1. 숫자들을 입력받는다.

2. 각 숫자를 하나의 이진트리로 표현할 수 있는지의 여부를 반환한다.

 

<해법>

1. 이진트리로 표현할 수 있는 수와 없는 수의 규칙 찾기

=> 번뜩이는 알고리즘이 떠오르지 않습니다. 따라서, 예제를 보면서 하나하나씩 직접 해보며 규칙을 찾아야합니다. 

7 = 111 (O)

42 = 0101010 (O)

5 = 101 (X)

그림과 쉽게 비교하기 위해 더미노드(0)도 추가했습니다. 규칙이 조금씩 보이시나요? 읽는 순서를 고려했을 때, 루트노드는 항상 2진수의 중앙에 옵니다. 루트노드에 더미노드(0)이 올 수 있을까요? 없습니다. 루트노드가 0인 수는 0뿐인데, 주어지는 숫자는 1이상 이기 때문입니다. 따라서, 규칙 하나를 찾았습니다. '2진수의 중앙은 0이 될 수 없다.'

 

두 번째 예시를 보며 규칙을 찾아보겠습니다. 

63 = 0111111 (O)

111 = 1101111 (O)

95 = 1011111 (X)

95는 2진수의 중앙이 1인데 불가능하다고 나옵니다. 왜일까요?

위와 같이 그려지기 때문입니다. 규칙 하나를 더 찾았습니다. '0이 1의 부모노드로 존재할 수 없다.' 또한, 그림을 직접 그려보면 트리를 왼쪽, 오른쪽 서브트리로 분할하고 정복해서 풀어야겠다는 느낌을 강하게 받을 수 있습니다. 그렇다면, 이제 어떻게 분할정복할지 알아보겠습니다.

 

2. 분할정복하는 방법

=> 위의 예시들로 몇가지 규칙을 찾았습니다. 찾은 규칙을 바탕으로 자세하게 분할정복하는 방법을 알아보겠습니다. 110 = 1101111을 예시로 들겠습니다.

(1) 분할하는 방법

110(왼쪽 서브트리) / 1(부모) / 111(오른쪽 서브트리) 로 분할합니다. 이후 왼쪽과 오른쪽을 정복한 후 이진트리로 표현할 수 있는지 판단합니다.

(2) 부모가 1인 경우

일단 이진트리로 표현할 수 있습니다. 왼쪽과 오른쪽 서브트리 또한 이진트리로 표현할 수 있다면, 이진트리로 표현할 수 있습니다.

(3) 부모가 0인 경우

부모가 0인 경우는 모두 불가능할까요? 아닙니다.

위와 같이 자식도 모두 0인 경우에는 이진트리로 표현할 수 있기 때문입니다. 따라서, 부모가 0인 경우에는 자식이 모두 0인 경우에는 가능하고, 나머지 경우는 불가능합니다.

 

3. 구현 전 정리

=> 아래는 구현 전 정리한 생각입니다.

(1) 주어진 숫자를 이진수로 만든다. (ex 5 -> 101) (코드의 hexToDec함수 참고)

(2) 만든 이진수를 포화 이진수로 만든다. 포화 이진수란 포화 이진트리로 표현할 수 있는 숫자이다. 포화이진트리의 노드 갯수는 2^n - 1개 입니다. 따라서, 포화이진트리 노드의 갯수에 맞춰서 앞에 0을 추가한다. (ex 11 -> 1011 -> 0001011) (코드의 changeToFullDec함수 참고)

(3) 만든 포화 이진수를 분할정복한다. 왼쪽, 중앙, 오른쪽으로 분할해서 정복한다. (ex 0001011 -> 000 / 1 / 011) (코드의 canDraw함수 참고)

자세한 구현은 아래 코드를 참고해주세요.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
#include <iostream>
#include <string>
#include <vector>
 
using namespace std;
 
string hexToDec(long long num);
string changeToFullDec(string str);
bool isAllZero(string str);
bool canDraw(string str);
 
vector<int> solution(vector<long long> numbers) {
    vector<int> answer;
    for (long long& num : numbers) {
        string dec = hexToDec(num);
        string fullDec = changeToFullDec(dec);
        answer.push_back(canDraw(fullDec));
    }
    return answer;
}
 
string hexToDec(long long num) {
    string ret = "";
    while (num) {
        ret = to_string(num % 2+ ret;
        num /= 2;
    }
    return ret;
}
 
string changeToFullDec(string str) {
    string ret = str;
    int idx = 2;
    while (true) {
        if (str.length() <= idx - 1break;
        idx *= 2;
    }
    for (int i = 0; i < idx - 1 - str.length(); i++) ret = "0" + ret;
    return ret;
}
 
bool canDraw(string str) {
    if (str.length() == 1 || isAllZero(str)) return true;
 
    int midIdx = str.length() / 2;
    string left = str.substr(0, midIdx);
    string right = str.substr(midIdx + 1);
 
    if (str[midIdx] == '1' && canDraw(left) && canDraw(right)) return true;
    else return false;
}
 
bool isAllZero(string str) {
    for (char c : str) {
        if (c != '0'return false;
    }
    return true;
}

 

분할정복에 대해 알아볼 수 있는 문제였습니다.

https://school.programmers.co.kr/learn/courses/30/lessons/150368

 

프로그래머스

코드 중심의 개발자 채용. 스택 기반의 포지션 매칭. 프로그래머스의 개발자 맞춤형 프로필을 등록하고, 나와 기술 궁합이 잘 맞는 기업들을 매칭 받으세요.

programmers.co.kr

<풀이>

1. 사용자 정보와 이모티콘 정보를 입력받는다.

2. 행사 목적의 최대치를 반환한다.

 

<해법>

1. 알고리즘 선택하기

=> 문제를 읽고 가장 먼저 떠오르는 알고리즘은 모든 경우를 다 해보는 브루트 포스 입니다. 브루트 포스를 사용할 수 있는지 확인하기 위해 시간복잡도를 계산해야합니다. 10, 20, 30, 40%로 7개의 이모티콘 모두 적용해 보아야하고 모든 사용자 100명에 있어서 7개의 이모티콘을 확인해봐야합니다. 시간 복잡도는 O(4^7 x (100 x 7)) = O(11,468,800) 입니다. 따라서, 브루트 포스 알고리즘을 사용할 수 있고 선택했습니다.

 

2. 구현 전 정리

=> 아래는 구현 전 정리한 생각입니다.

(1) 브루트 포스 알고리즘을 사용한다. 7개의 이모티콘에 10, 20, 30, 40% 할인 비율을 모두 적용시키는 것은 재귀함수로 구현한다.

(2) 이모티콘의 가격은 100의 배수로 주어지므로, 가격 연산은 편하게 진행하면 된다.

자세한 구현은 아래 코드를 참고해주세요.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
#include <iostream>
#include <string>
#include <vector>
 
using namespace std;
 
int N, M;
int rate[7];
vector<vector<int>> u;
vector<int> e;
int ansService, ansProfit;
vector<int> answer;
 
int calcPrice(int price, int discountRate) {
    return (price * (100 - discountRate)) / 100;
}
 
void selectDiscountRate(int idx) {
    if (idx == M) {
        int service = 0, profit = 0;
        for (int i = 0; i < N; i++) {
            int price = 0;
            for (int j = 0; j < M; j++) {
                if (rate[j] >= u[i][0]) price += calcPrice(e[j], rate[j]);
            }
            if (price >= u[i][1]) service++;
            else profit += price;
        }
 
        if (service > ansService) {
            ansService = service, ansProfit = profit;
        }
        else if (service == ansService) {
            if (profit > ansProfit) ansProfit = profit;
        }
        return;
    }
    
    for (int i = 10; i <= 40; i += 10) {
        rate[idx] = i;
        selectDiscountRate(idx + 1);
    }
}
 
vector<int> solution(vector<vector<int>> users, vector<int> emoticons) {
    //초기화
    u = users, e = emoticons;
    N = u.size(), M = e.size();
    ansService = 0, ansProfit = 0;
 
    //해법
    selectDiscountRate(0);
    answer.push_back(ansService);
    answer.push_back(ansProfit);
 
    //반환
    return answer;
}

 

브루트 포스에 대해 알아볼 수 있는 문제였습니다.

https://school.programmers.co.kr/learn/courses/30/lessons/150369

 

프로그래머스

코드 중심의 개발자 채용. 스택 기반의 포지션 매칭. 프로그래머스의 개발자 맞춤형 프로필을 등록하고, 나와 기술 궁합이 잘 맞는 기업들을 매칭 받으세요.

programmers.co.kr

<풀이>

1. 배달해야할 박스 갯수와 수거해야할 박스 갯수 정보를 입력받는다.

2. 모든 배달과 수거를 마치고 물류창고로 돌아오는 최소거리를 반환한다.

 

<해법>

1. 알고리즘 선택하기

=> 가장 끝에서 부터 가능한 배달과 수거를 최대로 하면 정답을 구할 수 있습니다. 따라서, 저는 그리디 알고리즘을 선택했습니다.

 

2. 자료구조 선택하기

=> 가장 끝에서 부터 배달과 수거를 진행합니다. 그리고 배달과 수거가 완료된 집은 넘어갑니다. 따라서, 저는 후입선출 자료구조인 스택을 선택했습니다.

 

3. 구현 전 정리

=> 아래는 구현 전 정리한 생각입니다.

(1) 끝 집에서 부터 배달과 수거를 진행한다. 이동한 거리는 max(배달해야할 끝집, 수거해야할 끝집) x 2 이다.

(2) 배달해야하는 집, 수거해야하는 집을 스택으로 관리한다. 

자세한 구현은 아래 코드를 참고해주세요.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
#include <iostream>
#include <string>
#include <vector>
#include <stack>
#include <algorithm>
 
using namespace std;
 
struct house {
    int idx, boxCnt;
};
 
stack<house> del, pic;
 
long long solution(int cap, int n, vector<int> deliveries, vector<int> pickups) {
    long long answer = 0;
 
    for (int i = 0; i < deliveries.size(); i++) {
        int boxCnt = deliveries[i];
        if (boxCnt) {
            del.push({ i + 1, boxCnt });
        }
    }
    for (int i = 0; i < pickups.size(); i++) {
        int boxCnt = pickups[i];
        if (boxCnt) {
            pic.push({ i + 1, boxCnt });
        }
    }
 
    while (true) {
        if (del.empty() && pic.empty()) break;
 
        int delIdx = del.empty() ? 0 : del.top().idx;
        int picIdx = pic.empty() ? 0 : pic.top().idx;
        answer += static_cast<long long>(max(delIdx, picIdx)) * 2;
 
        int delBoxCnt = 0;
        while (!del.empty()) {
            house& h = del.top();
            if (delBoxCnt + h.boxCnt <= cap) {
                delBoxCnt += h.boxCnt;
                del.pop();
            }
            else {
                h.boxCnt -= (cap - delBoxCnt);
                break;
            }
        }
 
        int picBoxCnt = 0;
        while (!pic.empty()) {
            house& h = pic.top();
            if (picBoxCnt + h.boxCnt <= cap) {
                picBoxCnt += h.boxCnt;
                pic.pop();
            }
            else {
                h.boxCnt -= (cap - picBoxCnt);
                break;
            }
        }
    }
 
    return answer;
}

 

그리디 알고리즘에 대해 알아볼 수 있는 문제였습니다.

https://school.programmers.co.kr/learn/courses/30/lessons/150370

 

프로그래머스

코드 중심의 개발자 채용. 스택 기반의 포지션 매칭. 프로그래머스의 개발자 맞춤형 프로필을 등록하고, 나와 기술 궁합이 잘 맞는 기업들을 매칭 받으세요.

programmers.co.kr

<풀이>

1. 현재 날짜, 약관 정보, 개인 정보를 입력받는다.

2. 유효기간이 지난 개인 정보의 번호를 반환한다.

 

<해법>

1. 날짜 문자열 연산 및 대소비교 방법

=> 위 문제는 "개인정보 수집 날짜 + 유효기간  < 오늘 날짜" 를 물어보는 것입니다. 이제 핵심은 날짜 연산 및 대소비교 입니다. 날짜 문자열을 가장 쉽게 연산과 대소비교를 하는 방법은 모두 정수로 바꿔서 계산하는 것입니다. 위 문제의 경우 모두 일 수로 바꾸면 연산과 대소비교를 빠르게 할 수 있습니다. 코드의 dateStringToInt 함수를 참고해주세요. 날짜나 시간 등등 문자열을 쉽고 빠르게 연산하고 대소비교하는 방법은 정수로 바꾸는 것입니다.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
#include <iostream>
#include <string>
#include <vector>
#include <map>
#include <sstream>
 
using namespace std;
 
map<charint> m;
 
int dateStringToInt(string str) {
    int year = stoi(str.substr(04));
    int month = stoi(str.substr(52));
    int day = stoi(str.substr(8));
    return (28 * 12 * year) + (28 * (month - 1)) + day;
}
 
int calcDate(int d, int num) {
    return d + (28 * num) - 1;
}
 
vector<int> solution(string today, vector<string> terms, vector<string> privacies) {
    vector<int> answer;
 
    int td = dateStringToInt(today);
 
    for (string& t : terms) {
        istringstream iss(t);
        char cmd;
        int num;
        iss >> cmd >> num;
        m[cmd] = num;
    }
 
    for (int i = 0; i < privacies.size(); i++) {
        istringstream iss(privacies[i]);
        string s;
        char cmd;
        iss >> s >> cmd;
 
        int d = dateStringToInt(s);
        int expired = calcDate(d, m[cmd]);
        if (expired < td) answer.push_back(i + 1);
    }
 
    return answer;
}

 

문자열에 대해 알아볼 수 있는 문제였습니다.

두 번째 풀이입니다. 예전과는 어떻게 얼마나 달라졌는지 궁금해서 한번 더 풀어보았습니다.

https://swexpertacademy.com/main/code/problem/problemDetail.do?contestProbId=AWXRQm6qfL0DFAUo 

 

SW Expert Academy

SW 프로그래밍 역량 강화에 도움이 되는 다양한 학습 컨텐츠를 확인하세요!

swexpertacademy.com

<풀이>

1. 벽돌 보드를 입력받는다.

2. N개의 구슬을 쏘아서 가장 많은 벽돌이 제거되는 경우를 찾고, 그 때 남은 벽돌의 개수를 출력한다.

 

<해법>

1. 알고리즘 선택하기

=> 구슬을 쏘는 모든 경우를 다 해보아야 합니다. 따라서, 저는 브루트포스를 선택했습니다. 

 

2. 벽돌 깨트리기

=> 저는 DFS를 선택했습니다. 4방향을 벽돌의 power만큼 탐색하면서 벽돌을 깨트렸습니다. 사실 구현은 DFS, BFS 큰 상관이 없다고 생각합니다.

 

3. 벽돌 내리기

=> 벽돌을 깨트렸으면, 이제 벽돌을 빈공간으로 내려야합니다. 저는 큐(Queue)를 선택했습니다. 아래서 부터 벽돌인 것만 큐에 차례로 담고, 나중에 다시 벽돌을 쌓는 개념으로 구현하였습니다. 자세한 구현은 코드의 fallBrick() 함수를 참조해주세요.

 

4. 구현 전 총정리

=> 아래는 구현 전 정리한 생각입니다.

(1) 구슬을 쏘는 위치는 재귀함수로 구현한다. 최악의 경우 12P4 이므로, 시간초과하지 않는다.

(2) 구슬의 위치를 다 결정하고 시뮬레이션을 할까, 구슬의 위치를 정할 때마다 시뮬레이션을 할까?

: 구슬의 위치를 정할 때마다 시뮬레이션을 진행한다. 구슬의 위치 [1,2,3,4]와 [1,2,3,5]는 구슬을 [1,2,3] 떨어트렸을 때까지 상황이 동일하다. 따라서, 시간을 줄이기 위해 구슬의 위치 정하고 바로 구슬을 떨어트려본다.

(3) 벽돌 깨트리는 것은 재귀함수로 구현한다.

(4) 벽돌 내릴 때는 큐를 이용해 구현한다.

(5) 남은 벽돌 개수의 최소를 구하는 것이므로, 반대로 얘기하면 깨트린 벽돌 개수의 최대를 구하는 것과 같다. 따라서, (처음 벽돌 개수) - (깨트린 벽돌 개수 max)를 출력한다.

자세한 구현은 아래 코드를 참조해주세요.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
#include <iostream>
#include <string.h>
#include <algorithm>
#include <queue>
#define INF 987654321
 
using namespace std;
 
int dx[] = { -1,0,1,0 };
int dy[] = { 0,1,0,-1 };
 
int N, W, H;
int map[15][12];
int totalBricks;
int answer;
 
bool isInner(int x, int y) {
    if (x < 0 || y < 0 || x >= H || y >= W) return false;
    return true;
}
 
int removeBrick(int x, int y) {
    int power = map[x][y];
    map[x][y] = 0;
    int ret = 1;
    for (int i = 0; i < 4; i++) {
        for (int j = 1; j < power; j++) {
            int nx = x + (dx[i] * j);
            int ny = y + (dy[i] * j);
            if (!isInner(nx, ny)) break;
            if (map[nx][ny] > 0) {
                ret += removeBrick(nx, ny);
            }
        }
    }
    return ret;
}
 
void fallBrick() {
    for (int j = 0; j < W; j++) {
        queue<int> q;
        for (int i = H - 1; i >= 0; i--) {
            if (map[i][j] > 0) q.push(map[i][j]);
            map[i][j] = 0;
        }
        int col = H - 1;
        while (!q.empty()) {
            map[col][j] = q.front();
            q.pop();
            col--;
        }
    }
}
 
int simulation(int col) {
    int removedBricks = 0;
    for (int i = 0; i < H; i++) {
        if (map[i][col] > 0) {
            removedBricks = removeBrick(i, col);
            fallBrick();
            break;
        }
    }
    return removedBricks;
}
 
int selectBallPos(int cnt) {
    if (cnt == N) return 0;
 
    int ret = 0;
    int backup[15][12];
    memcpy(backup, map, sizeof(backup));
    for (int i = 0; i < W; i++) {
        int removedBricks = simulation(i);
        ret = max(ret, selectBallPos(cnt + 1+ removedBricks);
        memcpy(map, backup, sizeof(map));
    }
    return ret;
}
 
void solution() {
    answer = totalBricks - selectBallPos(0);
}
 
int main() {
    int test_case;
    int T;
    cin >> T;
    for (test_case = 1; test_case <= T; test_case++) {
        //초기화
        N = 0, W = 0, H = 0;
        memset(map, 0sizeof(map));
        totalBricks = 0;
        answer = 0;
 
        //입력
        cin >> N >> W >> H;
        for (int i = 0; i < H; i++) {
            for (int j = 0; j < W; j++) {
                cin >> map[i][j];
                if (map[i][j] > 0) totalBricks++;
            }
        }
 
        //해법
        solution();
 
        //출력
        cout << "#" << test_case << " " << answer << "\n";
    }
    //종료
    return 0;
}

 

브루트포스, DFS, 시뮬레이션에 대해 알아볼 수 있는 문제였습니다.

https://swexpertacademy.com/main/code/problem/problemDetail.do?contestProbId=AV141J8KAIcCFAYD&categoryId=AV141J8KAIcCFAYD&categoryType=CODE&problemTitle=1232&orderBy=FIRST_REG_DATETIME&selectCodeLang=ALL&select-1=&pageSize=10&pageIndex=1 

 

SW Expert Academy

SW 프로그래밍 역량 강화에 도움이 되는 다양한 학습 컨텐츠를 확인하세요!

swexpertacademy.com

<풀이>

1. 트리를 입력받는다

2. 트리를 연산한 결과를 출력한다.

 

<해법>

1. 트리를 구현하는 방법

=> 저는 노드 구조체를 만들어서 구현하였습니다. 구조체에는 data와 왼쪽 자식, 오른쪽자식의 index가 있습니다.

 

2. 트리를 연산하는 방법

=> 저는 트리를 후위순회 하였습니다. 또한, 숫자는 leaf노드에만 존재하므로 노드의 데이터가 연산자면 자식노드들을 탐색하고, 숫자노드면 바로 숫자 데이터를 return하는 것으로 구현하였습니다.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
#include <iostream>
#include <vector>
#include <string>
 
using namespace std;
 
struct node {
    string data;
    int leftNode, rightNode;
};
 
int N;
node arr[1001];
int answer;
 
bool isOper(string s) {
    if (s == "+" || s == "-" || s == "*" || s == "/"return true;
    return false;
}
 
double calculate(double a, string oper, double b) {
    double output = 0;
    if (oper == "+") output = a + b;
    else if (oper == "-") output = a - b;
    else if (oper == "*") output = a * b;
    else output = a / b;
    return output;
}
 
double postOrder(int idx) {
    if (!isOper(arr[idx].data)) return stoi(arr[idx].data);
 
    double ret = 0;
    ret = calculate(postOrder(arr[idx].leftNode), arr[idx].data, postOrder(arr[idx].rightNode));
    return ret;
}
 
void solution() {
    answer = postOrder(1);
}
 
int main() {
    int test_case;
    int T;
    T = 10;
    for (test_case = 1; test_case <= T; test_case++) {
        //초기화
        N = 0;
        for (int i = 0; i < 1001; i++) arr[i] = { "",0,0 };
        answer = 0;
 
        //입력
        cin >> N;
        for (int i = 0; i < N; i++) {
            int idx;
            string data;
            cin >> idx >> data;
            arr[idx].data = data;
            if (isOper(data)) {
                int leftNode, rightNode;
                cin >> leftNode >> rightNode;
                arr[idx].leftNode = leftNode, arr[idx].rightNode = rightNode;
            }
        }
 
        //해법
        solution();
 
        //출력
        cout << "#" << test_case << " " << answer << "\n";
    }
    //종료
    return 0;
}

 

자료구조 트리와 재귀함수에 대해 알아볼 수 있는 문제였습니다.

+ Recent posts