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

 

SW Expert Academy

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

swexpertacademy.com

<풀이>

1. 전체 맵을 입력받는다.

2. 해당 맵에 건설할 수 있는 활주로의 개수를 출력한다.

 

<해법>

1. 문제를 간소화

=>

결국 위 그림을 묻는 문제입니다. 2차원상에서 가로 세로를 모두 따지려면 index가 헷갈립니다. 따라서 저는 1차원으로 옮겨서 생각했고, 이 1차원 지형에 활주로를 건설할 수 있는지 판단하는 함수를 만들어 해결하였습니다.

 

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
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
114
115
116
117
118
119
120
121
122
#include <iostream>
#include <string.h>
#include <vector>
 
using namespace std;
 
int N, X;
int map[20][20];
bool built[20];
int answer;
 
bool isInner(int i) {
    if (i < 0 || i >= N) return false;
    return true;
}
 
//경사로를 설치하라 수 있는지 확인하는 함수
bool canBuildSlope(vector<int> v, int start, int height) {
    /*
    경사로를 설치할 수 있는 조건
    - 지형 범위 안
    - 경사로가 설치된 곳 X
    - start부터 X만큼 높이가 height이어야 함
    */
    for (int i = start; i < start + X; i++) {
        if (isInner(i) && !built[i] && height == v[i]) {
            continue;
        }
        else return false;
    }
    return true;
}
 
void buildSlope(int start) {
    for (int i = start; i < start + X; i++) {
        built[i] = true;
    }
}
 
//지형이 v인 곳에 활주로를 건설할 수 있는지 확인하는 함수
bool canBuildRoad(vector<int> v) {
 
    memset(built, falsesizeof(built));
 
    int height = v[0];
    for (int i = 0; i < N; i++) {
        //1. 현재 높이 = 이전 높이
        if (v[i] == height) continue;
 
        //2. 현재 높이 = 이전 높이 + 1
        else if (v[i] == height + 1) {
            //경사로를 설치할 수 있는지 확인
            if (canBuildSlope(v, i - X, v[i] - 1)) {
                buildSlope(i - X);
            }
            else return false;
        }
 
        //3. 현재 높이 = 이전 높이 - 1
        else if (v[i] == height - 1) {
            //경사로를 설치할 수 있는지 확인
            if (canBuildSlope(v, i, v[i])) {
                buildSlope(i);
            }
            else return false;
        }
        else return false;
        height = v[i];
    }
    return true;
}
 
int main() {
    int test_case;
    int T;
 
    cin >> T;
 
    for (test_case = 1; test_case <= T; test_case++) {
 
        //초기화
        N = 0, X = 0;
        memset(map, 0sizeof(map));
        memset(built, falsesizeof(built));
        answer = 0;
 
        //입력
        cin >> N >> X;
        for (int i = 0; i < N; i++) {
            for (int j = 0; j < N; j++) {
                cin >> map[i][j];
            }
        }
 
        /* 해법 */
        //1. 가로 방향 체크
        for (int i = 0; i < N; i++) {
            vector<int> v;
            for (int j = 0; j < N; j++) {
                v.push_back(map[i][j]);
            }
 
            if (canBuildRoad(v)) answer++;
        }
 
        //2. 세로 방향 체크
        for (int i = 0; i < N; i++) {
            vector<int> v;
            for (int j = 0; j < N; j++) {
                v.push_back(map[j][i]);
            }
 
            if (canBuildRoad(v)) answer++;
        }
 
        //출력
        cout << "#" << test_case << " " << answer << "\n";
    }
 
    //종료
    return 0;
}

 

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

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

 

SW Expert Academy

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

swexpertacademy.com

<풀이>

1. A음식과 B음식을 만들 식재료를 분배한다.

2. 각 음식의 시너지 합을 구하고 그 차이가 최소가 되는 값을 출력한다.

 

<해법>

1. 식재료를 분배하는 방법

=> 식재료 중 절반을 선택해서 A음식에 사용하고, 선택하지 않은 나머지 절반을 B음식에 사용하도록 합니다. 간단히 생각하면, N개 중에서 N/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
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
#include <iostream>
#include <vector>
#include <string.h>
#include <algorithm>
#define INF 9876543321
 
using namespace std;
 
int N;
int S[16][16];
vector<int> a;
vector<int> b;
bool selected[16];
int answer;
 
int calculateTasteDiff() {
 
    int aTaste = 0, bTaste = 0;
 
    for (int i = 0; i < N / 2; i++) {
        for (int j = 0; j < N / 2; j++) {
            aTaste += S[a[i]][a[j]];
            bTaste += S[b[i]][b[j]];
        }
    }
 
    return abs(aTaste - bTaste);
}
 
void setiing() {
    a.clear(), b.clear();
    for (int i = 0; i < N; i++) {
        if (selected[i]) {
            a.push_back(i);
        }
        else {
            b.push_back(i);
        }
    }
}
 
void divideIngredient(int start, int cnt) {
 
    if (cnt == N / 2) {
        setiing();
        int tasteDiff = calculateTasteDiff();
        answer = min(answer, tasteDiff);
        return;
    }
 
    for (int i = start; i < N; i++) {
        selected[i] = true;
        divideIngredient(i + 1, cnt + 1);
        selected[i] = false;
    }
}
 
int main() {
    int test_case;
    int T;
 
    cin >> T;
 
    for (test_case = 1; test_case <= T; test_case++) {
 
        //초기화
        N = 0;
        memset(S, 0sizeof(S));
        a.clear();
        b.clear();
        memset(selected, falsesizeof(selected));
        answer = INF;
 
        //입력
        cin >> N;
        for (int i = 0; i < N; i++) {
            for (int j = 0; j < N; j++) {
                cin >> S[i][j];
            }
        }
 
        //해법
        divideIngredient(00);
 
        //출력
        cout << "#" << test_case << " " << answer << "\n";
    }
 
    //종료
    return 0;
}

 

재귀함수에 대해 알아볼 수 있는 문제였습니다.

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

 

SW Expert Academy

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

swexpertacademy.com

<풀이>

1. 원소들의 정보를 입력받는다.

2. 남은 원소들이 더이상 충돌하지 않을 때까지, 충돌한 원소들의 에너지의 합을 구해서 출력한다.

 

<해법>

1. 시뮬레이션 문제 X

=> 문제에서도 써있듯이, 처음에는 시뮬레이션 문제라고 생각했습니다. 저는 시간복잡도를 계산한 후에 시뮬레이션으로 풀면 안되겠다고 생각했습니다. 저는 시간복잡도를 1000초 x 원소 끼리의 충돌(1000 x 1000) = 1,000,000,000(10억)으로 계산했습니다. (문제를 다 푼 이후에 알게된 사실이지만, 이 문제를 시뮬레이션으로 풀 수 있고 많은 분들이 그렇게 풀었습니다.)

 

2. 수학적인 접근

=> 아래의 예시를 보겠습니다.

위 그림에서 볼 수 있듯이, 1000번의 시뮬레이션을 하지 않고 두 원소가 충돌한다는 사실을 수학적으로 알 수 있습니다. (원소1과 원소2의 방향, x좌표 차이, y좌표 차이 등을 통해 구합니다. 코드의 canCrash() 함수를 참조해주세요.)

따라서, 모든 원소들을 탐색하며 다른 원소들과의 충돌여부를 구해서 에너지를 더해주면 됩니다.

 

3. 두 원소가 정말 충돌할까?

=> 아래의 예시를 보겠습니다.

A1을 탐색하면 A3와 충돌한다고 나옵니다. 하지만 실제로 A3는 그 전에 A2와 충돌해서 사라지고, A1은 충돌하지 못합니다. 여기서 충돌 여부뿐만 아니라 충돌 좌표충돌 시간 정보도 필요하다는 것을 알 수 있습니다. 

 

4. 해법 순서 및 구현

(1) 해법 순서

① 모든 원소를 탐색한다.

② 두 원소가 충돌하면, 충돌 좌표와 충돌 시간을 구한다.

③ 같은 시간에 같은 좌표에서 충돌하는 원소들끼리 모은다.

④ 시간 오름차순으로 정리하고, 원소들을 충돌시킨다. 이전에 원소가 충돌했는지 확인하고, 두 개 이상의 원소가 충돌할 경우에만 충돌시키고 에너지를 방출한다.

 

(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
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
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
#include <iostream>
#include <string.h>
#include <set>
#include <map>
#include <vector>
 
using namespace std;
 
//구조체 : 위치(x좌표, y좌표)
struct pos {
    int x, y;
};
 
//구조체 : 원자(원자 위치, 방향, 에너지)
struct atom {
    pos p;
    int dir;
    int energy;
};
 
//구조체 : 충돌(충돌 위치, 시간)
struct crash {
    pos p;
    int time;
};
 
//map의 key를 time이 작은 순서대로 정렬하기 위해
bool operator<(crash a, crash b) {
    if (a.time == b.time) {
        if (a.p.x == b.p.x) {
            return a.p.y < b.p.y;
        }
        return a.p.x < b.p.x;
    }
    return a.time < b.time;
}
 
int N;
vector<atom> atoms;
map<crash, set<int>> crashes;
bool crashed[1000];
int answer;
 
//원자 a1과 a2가 충돌할 수 있는지 판단하는 함수
bool canCrash(atom a1, atom a2) {
    int xGap = abs(a1.p.x - a2.p.x);
    int yGap = abs(a1.p.y - a2.p.y);
    switch (a1.dir) {
    case 0:
        if ((a2.dir == 1 && a1.p.x == a2.p.x && a1.p.y < a2.p.y) ||
            (a2.dir == 2 && a1.p.x < a2.p.x && a1.p.y < a2.p.y && xGap == yGap) ||
            (a2.dir == 3 && a1.p.x > a2.p.x && a1.p.y < a2.p.y && xGap == yGap)) return true;
        else return false;
    case 1:
        if ((a2.dir == 0 && a1.p.x == a2.p.x && a1.p.y > a2.p.y) ||
            (a2.dir == 2 && a1.p.x < a2.p.x && a1.p.y > a2.p.y && xGap == yGap) ||
            (a2.dir == 3 && a1.p.x > a2.p.x && a1.p.y > a2.p.y && xGap == yGap)) return true;
        else return false;
    case 2:
        if ((a2.dir == 0 && a1.p.x > a2.p.x && a1.p.y > a2.p.y && xGap == yGap) ||
            (a2.dir == 1 && a1.p.x > a2.p.x && a1.p.y < a2.p.y && xGap == yGap) ||
            (a2.dir == 3 && a1.p.x > a2.p.x && a1.p.y == a2.p.y)) return true;
        else return false;
    case 3:
        if ((a2.dir == 0 && a1.p.x < a2.p.x && a1.p.y > a2.p.y && xGap == yGap) ||
            (a2.dir == 1 && a1.p.x < a2.p.x && a1.p.y < a2.p.y && xGap == yGap) ||
            (a2.dir == 2 && a1.p.x < a2.p.x && a1.p.y == a2.p.y)) return true;
        else return false;
    }
}
 
//원자 a1과 a2가 충돌할 수 있다면, 충돌하는 위치와 시간을 구하는 함수
crash findCrash(atom a1, atom a2) {
    crash output;
    
    int xGap = abs(a1.p.x - a2.p.x);
    int yGap = abs(a1.p.y - a2.p.y);
    output.time = (xGap + yGap) / 2;
    switch (a1.dir) {
    case 0:
        if (a2.dir == 1) output.p = { a1.p.x, a1.p.y + (yGap / 2) };
        else output.p = { a1.p.x, a1.p.y + yGap };
        break;
    case 1:
        if (a2.dir == 0) output.p = { a1.p.x, a1.p.y - (yGap / 2) };
        else output.p = { a1.p.x, a1.p.y - yGap };
        break;
    case 2:
        if (a2.dir == 3) output.p = { a1.p.x - (xGap / 2), a1.p.y };
        else output.p = { a1.p.x - xGap, a1.p.y };
        break;
    case 3:
        if (a2.dir == 2) output.p = { a1.p.x + (xGap / 2), a1.p.y };
        else output.p = { a1.p.x + xGap, a1.p.y };
        break;
    }
 
    return output;
}
 
int main() {
    int test_case;
    int T;
 
    cin >> T;
 
    for (test_case = 1; test_case <= T; test_case++) {
 
        //초기화
        N = 0;
        atoms.clear();
        crashes.clear();
        memset(crashed, falsesizeof(crashed));
        answer = 0;
 
        //입력
        cin >> N;
        for (int i = 0; i < N; i++) {
            int x, y, dir, energy;
            cin >> x >> y >> dir >> energy;
            atom a;
            a.p = { x * 2, y * 2 }; //좌표, 시간을 int로 관리하기 위해
            a.dir = dir;
            a.energy = energy;
            atoms.push_back(a);
        }
 
        /* 해법 */
        //1. 모든 원소들끼리의 충돌하는 지점과 시간을 구한다
        for (int i = 0; i < N; i++) {
            for (int j = i + 1; j < N; j++) {
                if (canCrash(atoms[i], atoms[j])) {
                    crash c = findCrash(atoms[i], atoms[j]);
                    crashes[c].insert({i, j});
                }
            }
        }
 
        //2. 충돌하는 시간이 작은 것부터 원소를 충돌시킨다
        for (auto c : crashes) {
            int count = 0;
            for (auto index : c.second) {
                if (!crashed[index]) count++;
            }
            //원소가 두 개 이상일 경우에 충돌한다
            if (count > 1) {
                for (auto index : c.second) {
                    crashed[index] = true;
                    answer += atoms[index].energy;
                }
            }
        }
 
        //출력
        cout << "#" << test_case << " " << answer << "\n";
    }
 
    //종료
    return 0;
}

 

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

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

 

SW Expert Academy

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

swexpertacademy.com

<풀이>

1. 자석 상태와 회전 정보를 입력받고, 시뮬레이션을 진행한다.

2. 이 후, 자석 점수를 계산하여 출력한다.

 

<해법>

1. 시뮬레이션 문제

=> 시뮬레이션 문제는 조건에 맞는 정확한 구현이 가장 중요합니다. 저는 코드를 짜기 전에 사용될 자료구조와, 조건에 맞는 구현 방법을 미리 생각합니다.

 

(1) 자료구조

① 자석 자료구조

: 원형으로 돌아가기 때문에, 맨 앞에있는 것과 맨 뒤에있는 것을 쉽게 넣고 뺄 수 있는 덱(deque)를 선택하였습니다.

덱은 위와 같은 구조로 되어있고, 자세한 것은 검색을 통해 알아보시면 좋을 것 같습니다.

저는 덱을 이용해서 위와같은 방식으로 동작하도록 구현하였습니다.

 

(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
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
#include <iostream>
#include <string.h>
#include <deque>
 
using namespace std;
 
int K;
deque<int> magnet[5];
bool visited[5];
int answer;
 
int calculateScore() {
    int output = 0;
    int cnt = 1;
    for (int i = 1; i <= 4; i++) {
        if (magnet[i][0== 1) {
            output += cnt;
        }
        cnt *= 2;
    }
    return output;
}
 
void rotate(int num, int dir) {
 
    visited[num] = true;
   
    //왼쪽 자석 돌리기
    if (num > 1 && magnet[num][6!= magnet[num - 1][2&& !visited[num - 1]) {
        rotate(num - 1-dir);
    }
 
    //오른쪽 자석 돌리기
    if (num < 4 && magnet[num][2!= magnet[num + 1][6&& !visited[num + 1]) {
        rotate(num + 1-dir);
    }
 
    //해당자석 칸 옮기기
    if (dir == 1) {
        magnet[num].push_front(magnet[num][7]);
        magnet[num].pop_back();
    }
    else {
        magnet[num].push_back(magnet[num][0]);
        magnet[num].pop_front();
    }
}
 
int main() {
    int test_case;
    int T;
 
    cin >> T;
 
    for (test_case = 1; test_case <= T; test_case++) {
 
        //초기화
        K = 0;
        for (int i = 0; i < 5; i++) {
            magnet[i].clear();
        }
        memset(visited, falsesizeof(visited));
        answer = 0;
 
        //입력
        cin >> K;
        for (int i = 1; i <= 4; i++) {
            for (int j = 0; j < 8; j++) {
                int num = 0;
                cin >> num;
                magnet[i].push_back(num);
            }
        }
        for (int i = 0; i < K; i++) {
            int num, dir = 0;
            cin >> num >> dir;
 
            //해법
            rotate(num, dir);
            memset(visited, falsesizeof(visited));
        }
 
        answer = calculateScore();
 
        //출력
        cout << "#" << test_case << " " << answer << "\n";
    }
 
    //종료
    return 0;
}

 

시뮬레이션과 재귀함수에 대해 알아볼 수 있는 문제였습니다.

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

 

SW Expert Academy

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

swexpertacademy.com

<풀이>

1. 고객정보와 창구정보를 입력받고, 주어진 시뮬레이션을 돌린다.

2. 지갑을 분실했을 가능성이 있는 고객들의 고객번호 합을 구해 출력한다.

 

<해법>

1. 시뮬레이션 문제

=> 시뮬레이션 문제는 조건에 맞는 정확한 구현이 가장 중요합니다. 저는 코드를 짜기 전에 사용될 자료구조와, 조건에 맞는 구현 방법을 미리 생각합니다.

 

(1) 자료구조

저는 위와같이 초기 자료구조를 구상하였습니다.

 

(2) 조건에 맞는 구현방법

① (접수창구) 여러 고객이 기다리고 있는 경우 고객번호가 낮은 순서대로 우선 접수한다.

: 초기 고객 정보입력받는 큐에 고객번호가 낮은 순서대로 들어가있으므로, 접수창구줄 큐에도 고객번호가 낮은 순서대로 들어가게 된다. 따라서, 큐의 앞부분 부터 차례대로 접수 시킵니다.

 

② (접수창구) 빈 창구가 여러 곳인 경우 접수 창구번호가 작은 곳으로 간다.

: 접수 창구 배열을 앞부분부터 탐색하면서, 빈 창구일 경우 고객을 넣습니다.

 

③ (정비 창구) 먼저 기다리는 고객이 우선한다.

: 정비 창구 줄이 큐 이므로 만족합니다.

 

④ (정비 창구) 두 명 이상의 고객들이 접수 창구에서 동시에 접수를 완료하고 정비 창구로 이동한 경우, 이용했던 접수 창구번호가 작은 고객이 우선한다.

: 접수 창구 배열을 앞부분부터 탐색하면서 완료된 고객을 큐에 넣습니다.

 

⑤ (정비 창구) 빈 창구가 여러 곳인 경우 정비 창구번호가 작은 곳으로 간다.

: 정비 창구 배열을 앞부분부터 탐색하면서, 빈 창구일 경우 고객을 넣습니다.

 

고객이 차량 정비소에 도착하여 접수 창구로 가는 시간 또는 접수 창구에서 정비 창구로 이동하는 시간은 무시할 수 있다. 즉, 이동 시간은 0이다.

: 동시에 이동한다는 뜻입니다. 하지만, 코드는 순차적으로 진행되기 때문에 교묘하게 동시처럼 보이게 해야합니다. 해결방법은 각 창구에서 완료된 사람을 먼저 빼낸 후에, 줄에 있는 사람을 창구에 배치합니다.

 

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
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
#include <iostream>
#include <string.h>
#include <queue>
#include <algorithm>
 
using namespace std;
 
struct customer {
    int index;
    int arriveTime;
    int timer;
    int a;
    int b;
};
 
struct desk {
    customer c;
    int time;
    bool isWorking;
};
 
int N, M, K, A, B;
desk aDesk[10];         //receptionDesk
desk bDesk[10];         //repairDesk
queue<customer> start;  //처음 입력받은 고객 명단
queue<customer> aLine;  //receptionDesk를 기다리는 줄
queue<customer> bLine;  //repairDesk를 기다리는 줄
int answer;
 
bool isWalletCustomer(customer c) {
    return (c.a == A && c.b == B) ? true : false;
}
 
void simulation() {
 
    int clock = 0;
    int finishCustomerNum = 0;
 
    while (true) {
 
        //시뮬레이션 종료조건
        if (finishCustomerNum == K) break;
 
        //start -> aLine
        while (!start.empty()) {
            customer c = start.front();
            if (c.arriveTime == clock) {
                aLine.push(c);
                start.pop();
            }
            else break;
        }
 
        //aDesk 고객 처리
        for (int i = 1; i <= N; i++) {
            //i번 창구에 고객이 있는 경우
            if (aDesk[i].isWorking) {
                //고객처리 완료했을 경우
                if (aDesk[i].c.timer == aDesk[i].time) {
                    aDesk[i].isWorking = false;
 
                    customer c = aDesk[i].c;
                    c.timer = 0;
                    bLine.push(c);
                }
                else {
                    aDesk[i].c.timer++;
                }
            }
        }
 
        //aLine -> aDesk
        for (int i = 1; i <= N; i++) {
            if (aLine.empty()) break;
            if (!aDesk[i].isWorking) {
                customer c = aLine.front();
                aLine.pop();
 
                c.a = i;
                c.timer++;
 
                aDesk[i].c = c;
                aDesk[i].isWorking = true;
            }
        }
 
        //bDesk 고객 처리
        for (int i = 1; i <= M; i++) {
            //i번 창구에 고객이 있는 경우
            if (bDesk[i].isWorking) {
                //고객처리 완료했을 경우
                if (bDesk[i].c.timer == bDesk[i].time) {
                    bDesk[i].isWorking = false;
                    customer c = bDesk[i].c;      
                    //지갑 분실 고객일 경우
                    if (isWalletCustomer(c)) {
                        if (answer == -1) answer = 0;
                        answer += c.index;
                    }
                    finishCustomerNum++;
                }
                else {
                    bDesk[i].c.timer++;
                }
            }
        }
 
        //bLine -> bDesk
        for (int i = 1; i <= M; i++) {
            if (bLine.empty()) break;
            if (!bDesk[i].isWorking) {
                customer c = bLine.front();
                bLine.pop();
 
                c.b = i;
                c.timer++;
 
                bDesk[i].c = c;
                bDesk[i].isWorking = true;
            }
        }
 
        clock++;
    }
}
 
int main() {
    int test_case;
    int T;
 
    cin >> T;
 
    for (test_case = 1; test_case <= T; test_case++) {
 
        //초기화
        N = 0, M = 0, K = 0, A = 0, B = 0;
        memset(aDesk, 0sizeof(aDesk));
        memset(bDesk, 0sizeof(bDesk));
        while (!start.empty()) start.pop();
        while (!aLine.empty()) aLine.pop();
        while (!bLine.empty()) bLine.pop();
        answer = -1;
 
        //입력
        cin >> N >> M >> K >> A >> B;
        for (int i = 1; i <= N; i++) {
            cin >> aDesk[i].time;
        }
        for (int i = 1; i <= M; i++) {
            cin >> bDesk[i].time;
        }
        for (int i = 1; i <= K; i++) {
            int arriveTime = 0;
            cin >> arriveTime;
            customer c = { i, arriveTime };
            start.push(c);
        }
 
        //해법
        simulation();
        
        //출력
        cout << "#" << test_case << " " << answer << "\n";
    }
 
    //종료
    return 0;
}

 

시뮬레이션에 대해 알아볼 수 있는 문제였습니다.

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

 

SW Expert Academy

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

swexpertacademy.com

<풀이>

1. 세포의 초기 상태와 배양 시간을 입력받고, 시뮬레이션을 돌린다.

2. 시간이 지난 후, 살아있는 줄기 세포의 갯수를 출력한다.

 

<해법>

단순한 시뮬레이션 처럼 보이지만, 문제를 풀 때 까다롭게하는 몇 가지 조건들이 있습니다. 이 조건들을 해결하는 저의 방법을 말씀드리겠습니다.

 

1. '배양 용기의 크기는 무한하다.'

=> 저는 이걸 읽고, '단순히 map[][], visited[][]와 같이 2차원 배열을 이용하는 것이 아니라, 다른 방식을 사용해서 정보를 저장해야 겠다.' 라는 생각을 하였습니다. 저는 set를 이용하여 visitied를 구현하였습니다.

2차원 배열을 이용하여 풀 수도 있습니다. 많은 분들이 2차원 배열을 이용하여 풀었습니다. 그렇지만, 저는 최대한 문제의 조건에 맞게 풀고 싶었습니다.(2차원 배열은 크기가 무한하지 않다고 생각해서...)

 

2. '여러개의 세포가 동시에 번식할 때, 생명력이 높은 세포가 셀을 차지한다.'

=> 저는 이걸 읽고, '단순히 큐에 넣고, 돌리는 방법만으로는 안된다.' 라는 생각을 하였습니다. 왜냐하면, 세포 간 우선순위가 존재하기 때문입니다. 저는 세포 생명력에 따라 큐 배열을 만들었고(생명력 최대치가 10이기 때문), 생명력이 높은 세포 큐부터 탐색을 진행해서 생명력이 높은 세포가 먼저 자리를 차지할 수 있도록 하였습니다.

 

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
114
115
116
117
118
#include <iostream>
#include <set>
#include <queue>
 
using namespace std;
 
//구조체 : 위치
struct pos { 
    int x, y;
};
 
//구조체 : 세포
struct cell {
    pos p;          //위치
    int timer;      //남은 생명
};
 
bool operator<(pos a, pos b) {
    if (a.x == b.x) {
        return a.y < b.y;
    }
    return a.x < b.x;
}
 
int dx[] = { -1,0,1,0 };
int dy[] = { 0,1,0,-1 };
 
int N, M, K;
int answer;
set<pos> visited;
queue<cell> q[11];
 
void simulation() {
    while (K--) {
 
        for (int i = 10; i > 0; i--) {
            int qSize = q[i].size();
 
            while (qSize--) {
 
                cell curCell = q[i].front();
                q[i].pop();
 
                //번식
                if (curCell.timer == 0) {
                    for (int j = 0; j < 4; j++) {
                        cell childCell = curCell;
                        childCell.p.x += dx[j];
                        childCell.p.y += dy[j];
                        childCell.timer = i;
 
                        //이미 세포가 차지하고 있는 경우
                        if (visited.find(childCell.p) != visited.end()) {
                            continue;
                        }
 
                        visited.insert(childCell.p);
                        q[i].push(childCell);
                    }
                }
 
                //세포 늙고, 아직 살아있는 경우만 다시 큐에 넣기
                cell nxtCell = curCell;
                nxtCell.timer--;
                if (nxtCell.timer != -i) {
                    q[i].push(nxtCell);
                }
            }
        }
    }
 
    //정답 갱신
    for (int i = 1; i < 11; i++) {
        answer += q[i].size();
    }
}
 
int main() {
    int test_case;
    int T;
 
    cin >> T;
 
    for (test_case = 1; test_case <= T; test_case++) {
 
        //초기화
        N = 0, M = 0, K = 0;
        answer = 0;
        visited.clear();
        for (int i = 1; i < 11; i++) {
            while (!q[i].empty()) {
                q[i].pop();
            }
        }
 
        //입력
        cin >> N >> M >> K;
        for (int i = 0; i < N; i++) {
            for (int j = 0; j < M; j++) {
                int lifeTime = 0;
                cin >> lifeTime;
                if (lifeTime > 0) {
                    visited.insert({ i,j });
                    q[lifeTime].push({ {i,j}, lifeTime });
                }
            }
        }
 
        //해법
        simulation();
 
        //출력
        cout << "#" << test_case << " " << answer << "\n";
    }
 
    //종료
    return 0;
}

 

구현과 BFS에 대해 알아볼 수 있는 문제였습니다.

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

 

SW Expert Academy

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

swexpertacademy.com

<풀이>

1. 한 지점에서 가로로 M만큼 꿀을 채취했을 때, 합은 C이하이면서 제곱의 합은 가장 큰 경우를 찾고, 그 때 제곱의 합을 다른 배열에 저장한다.(모든 지점에서 이 행위를 반복한다.)

2. (A양봉업자 최대이윤 + B양봉업자 최대이윤)의 최댓값을 구해서 출력한다.

 

<해법>

1. 한 지점에서 가로로 M만큼 꿀을 채취했을 때, 최대이윤을 구하는 방법

=> 문제를 간단하게 생각(합은 C이하, 제곱의 합이 가장 큰 경우 생각X)하면, 숫자들의 '조합'을 구하는 문제와 같습니다. 저는 재귀함수를 이용해서 먼저 조합을 구현하였고, 그 이후에 조건들을 덧붙여가며 문제를 해결했습니다.

 

(전체적인 문제를 해결한 흐름입니다.)

 

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
#include <iostream>
#include <string.h>
#include <vector>
#include <algorithm>
 
using namespace std;
 
int N, M, C;
int honeyMap[10][10];       //주어진 입력 맵
int maxProfitMap[10][10];   //최대 이윤 맵
 
//벡터 v의 원소들로 조합하여, 합은 C보다 작으면서 가장 큰 이윤을 찾는 함수
int findMaxProfit(vector<int> v, int idx, int sum, int profit) {
 
    if (sum > C) {
        return 0;
    }
 
    int maxProfit = 0;
    maxProfit = max(maxProfit, profit);
 
    if (idx >= M) {
        return maxProfit;
    }
 
    for (int i = idx; i < M; i++) {
        maxProfit = max(maxProfit, findMaxProfit(v, i + 1, sum + v[i], profit + (v[i] * v[i])));
    }
    
    return maxProfit;
}
 
int main() {
    int test_case;
    int T;
 
    cin >> T;
 
    for (test_case = 1; test_case <= T; test_case++) {
 
        //초기화
        N = 0, M = 0, C = 0;
        memset(honeyMap, 0sizeof(honeyMap));
        memset(maxProfitMap, 0sizeof(maxProfitMap));
        int answer = 0;
 
        //입력
        cin >> N >> M >> C;
        for (int i = 0; i < N; i++) {
            for (int j = 0; j < N; j++) {
                cin >> honeyMap[i][j];
            }
        }
 
        /* 해법 */
        //1. 각 지점에서 가로로 M만큼 꿀을 채취했을 때, 가장 많은 이윤을 찾아 저장
        for (int i = 0; i < N; i++) {
            for (int j = 0; j <= N - M; j++) {
                vector<int> v;
                for (int k = 0; k < M; k++) {
                    v.push_back(honeyMap[i][j + k]);
                }
                maxProfitMap[i][j] = findMaxProfit(v, 000);
            }
        }
 
        //2. (A양봉업자 최대이윤 + B양봉업자 최대이윤) 최댓값 구하기
        for (int i = 0; i < N; i++) {
            for (int j = 0; j <= N - M; j++) {
                int maxProfitSum = maxProfitMap[i][j];
 
                int maxProfit = 0;
                for (int k = i; k < N; k++) {
                    int startL = 0;
                    if (k == i) {
                        startL = j + M;
                    }
                    for (int l = startL; l <= N - M; l++) {
                        maxProfit = max(maxProfit, maxProfitMap[k][l]);
                    }
                }
 
                maxProfitSum += maxProfit;
                answer = max(answer, maxProfitSum);
            }
        }
 
        //출력
        cout << "#" << test_case << " " << answer << "\n";
    }
 
    //종료
    return 0;
}

 

재귀함수와 구현에 대해 알아볼 수 있는 문제였습니다.

swexpertacademy.com/main/code/problem/problemDetail.do?contestProbId=AV2b7Yf6ABcBBASw&categoryId=AV2b7Yf6ABcBBASw&categoryType=CODE

 

SW Expert Academy

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

swexpertacademy.com

<풀이>

1. 선반 높이와 점원들의 키를 입력받는다.

2. 점원들의 키를 사용하여 얻을 수 있는 선반 높이 중, 선반 높이보다 크면서 가장 가까운 값을 출력한다.

 

<해법>

1. 점원들이 만들 수 있는 탑 높이를 계산하는 방법

=> 모든 경우를 다 해보아야합니다. 각각의 점원을 사용할지 안할지를 정하고, 모든 점원의 사용여부가 결정되었을때 결과를 갱신합니다.

저는 위 그림과 같은 상태공간트리가 그려지도록 구현하였습니다.

 

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
#include <iostream>
#include <string.h>
#include <algorithm>
#define INF 987654321
 
using namespace std;
 
int N, B;
int H[20];
int answer;
 
void makeTower(int index, int sumHeight) {
 
    //종료조건
    if (index == N) {
        if (sumHeight >= B) {
            //결과 갱신
            answer = min(answer, sumHeight);
        }
        return;
    }
 
    //현재 탑을 쌓는 경우 해보기
    makeTower(index + 1, sumHeight + H[index]);
 
    //현재 탑을 쌓지 않는 경우 해보기
    makeTower(index + 1, sumHeight);
}
 
int main() {
 
    int test_case;
    int T;
 
    cin >> T;
 
    for (test_case = 1; test_case <= T; test_case++) {
 
        //초기화
        N = 0, B = 0;
        memset(H, 0sizeof(H));
        answer = INF;
 
        //입력
        cin >> N >> B;
        for (int i = 0; i < N; i++) {
            cin >> H[i];
        }
 
        //해법
        makeTower(00);
 
        //출력
        cout << "#" << test_case << " " << answer - B << "\n";
    }
 
    //종료
    return 0;
}
 

 

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

+ Recent posts