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

 

SW Expert Academy

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

swexpertacademy.com

<풀이>

1. 미로를 입력받는다.

2. 미로 출발점부터 도착점까지 갈 수 있는지 출력한다.

 

<해법>

1. 알고리즘 선택하기

=> 길찾기 알고리즘으로 DFS(스택, 재귀함수), BFS(큐)가 있습니다. 모든 방법으로 풀이가 가능합니다(모든 방법으로 풀어보았습니다). 저는 미로 1에서 DFS(재귀함수)를 이용한 코드를 올렸으므로, 이번에는 BFS를 이용한 코드를 올리겠습니다.

 

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
#include <iostream>
#include <string.h>
#include <queue>
 
using namespace std;
 
struct pos {
    int x, y;
};
 
int dx[] = { -1,0,1,0 };
int dy[] = { 0,1,0,-1 };
 
int trash;
char map[100][100];
pos start;
bool answer;
 
bool isInner(pos p) {
    if (p.x < 0 || p.y < 0 || p.x >= 100 || p.y >= 100return false;
    return true;
}
 
bool findRoute() {
    queue<pos> q;
    bool visited[100][100];
    memset(visited, falsesizeof(visited));
 
    q.push(start);
    visited[start.x][start.y] = true;
    while (!q.empty()) {
        pos cur = q.front();
        q.pop();
        if (map[cur.x][cur.y] == '3'return true;
        for (int i = 0; i < 4; i++) {
            pos nxt = cur;
            nxt.x += dx[i];
            nxt.y += dy[i];
            if (isInner(nxt) && map[nxt.x][nxt.y] != '1' && !visited[nxt.x][nxt.y]) {
                q.push(nxt);
                visited[nxt.x][nxt.y] = true;
            }
        }
    }
    return false;
}
 
void solution() {
    answer = findRoute();
}
 
int main() {
    int test_case;
    int T;
    T = 10;
    for (test_case = 1; test_case <= T; test_case++) {
        //초기화
        trash = 0;
        memset(map, '0'sizeof(map));
        start = { 0,0 };
        answer = false;
 
        //입력
        cin >> trash;
        for (int i = 0; i < 100; i++) {
            for (int j = 0; j < 100; j++) {
                cin >> map[i][j];
                if (map[i][j] == '2') {
                    start = { i,j };
                }
            }
        }
 
        //해법
        solution();
 
        //출력
        cout << "#" << test_case << " " << answer << "\n";
    }
 
    //종료
    return 0;
}

 

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

'알고리즘 문제풀이 > SWEA' 카테고리의 다른 글

[C++] SWEA 1230 - 암호문3  (0) 2022.12.24
[C++] SWEA 5644 - 무선 충전(2)  (0) 2022.12.24
[C++] SWEA 1225 - 암호생성기  (0) 2022.12.24
[C++] SWEA 5650 - 핀볼 게임(2)  (0) 2022.12.23
[C++] SWEA 1226 - 미로1  (0) 2022.12.21

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

 

SW Expert Academy

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

swexpertacademy.com

<풀이>

1. 8개의 숫자를 입력받는다.

2. 주어진 작업을 종료된 후 8개의 숫자를 출력한다.

 

<해법>

1. 하나하나씩 다 해봐야 할까?

=> 사실 이 문제를 푸는 것은 간단합니다. 큐를 이용해서 주어진 조건에 맞게 구현하는 것은 어렵지 않은 일입니다. 하지만, 테스트케이스처럼 큰 숫자가 나올경우 너무 오래걸릴거란 생각이 듭니다. 꼭 하나하나씩 1,2,3,4,5,1,2,3,4,5... 순서로 빼봐야할까요? 아닙니다. 잘 생각해보면, 8사이클이 돌 경우 모든 숫자에서 1+2+3+4+5 = 15의 숫자가 빠지게 됩니다. 따라서, 모든 숫자에서 15씩 동일하게 여러번 뺄 수 있습니다. 그렇지만, 여기서 조금 더 생각해서 숫자가 15,30,45 등등 15의 배수일 경우를 생각해야합니다. 이 때는 0이 될때까지 빼면 안됩니다. 왜냐하면 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
#include <iostream>
#include <string.h>
#include <queue>
#define INF 987654321
 
using namespace std;
 
int trash;
int arr[8];
queue<int> q;
int minimum;
 
void solution() {
    int div = minimum / 15;
    int remainder = minimum % 15;
    div = (remainder == 0) ? div - 1 : div;
    for (int i = 0; i < 8; i++) {
        arr[i] -= (15 * div);
    }
    for (int i = 0; i < 8; i++) {
        q.push(arr[i]);
    }
    while (true) {
        for (int i = 1; i <= 5; i++) {
            int tmp = q.front() - i;
            q.pop();
            q.push((tmp <= 0 ? 0 : tmp));
            if (tmp <= 0return;
        }
    }
}
 
int main() {
    int test_case;
    int T;
 
    T = 10;
 
    for (test_case = 1; test_case <= T; test_case++) {
 
        //초기화
        trash = 0;
        memset(arr, 0sizeof(arr));
        while (!q.empty()) q.pop();
        minimum = INF;
 
        //입력
        cin >> trash;
        for (int i = 0; i < 8; i++) {
            int num;
            cin >> num;
            arr[i] = num;
            minimum = min(minimum, num);
        }
 
        //해법
        solution();
 
        //출력
        cout << "#" << test_case << " ";
        while (!q.empty()) {
            cout << q.front() << " ";
            q.pop();
        }
        cout << "\n";
    }
 
    //종료
    return 0;
}

 

수학적인 사고와 구현에 대해 알아볼 수 있었습니다.

'알고리즘 문제풀이 > SWEA' 카테고리의 다른 글

[C++] SWEA 5644 - 무선 충전(2)  (0) 2022.12.24
[C++] SWEA 1227 - 미로2  (0) 2022.12.24
[C++] SWEA 5650 - 핀볼 게임(2)  (0) 2022.12.23
[C++] SWEA 1226 - 미로1  (0) 2022.12.21
[C++] SWEA 1224 - 계산기3  (0) 2022.12.21

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

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

 

SW Expert Academy

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

swexpertacademy.com

<풀이>

1. 게임판을 입력받는다.

2. 핀볼을 굴려서 얻을 수 있는 점수의 최댓값을 출력한다.

 

<해법>

1. 알고리즘 선택하기

=> 게임판의 0인 곳에서 4방향으로 핀볼을 모두 굴려봐야합니다. 따라서, 저는 브루트포스를 선택했습니다.

 

2. 시뮬레이션 구현하기

=> 조건에 맞게 정확하게 구현하기만 하면 되는 시뮬레이션입니다. 구현에 정답은 없지만, 저가 좀 더 쉽게 구현하기 위해 만들었던 장치들을 소개하겠습니다.

(1) 게임판을 102x102로 만든다.

게임판을 102x102로 만들면, 벽을 처리하는 구현이 간단해집니다. 왜냐하면 벽도 하나의 공간으로 생각할 수 있기 때문입니다. 

(2) 게임판 테두리를 5번 블록으로 채운다.

결국 게임판 외곽의 벽들은 핀볼의 방향을 반대방향으로 바꾸는 5번 블록과 성질이 같습니다. 게임판 외곽을 5번 블록으로 채우면 벽도 블록으로 생각할 수 있으므로 구현이 간단해집니다.

(3) 만나는 블록과 핀볼의 방향에 따라 바뀌는 핀볼 방향을 미리 2차원 배열로 구현한다.

미리 만들어 놓으면, 핀볼이 바뀌는 방향을 쉽게 구할 수 있습니다.

 

3. 최적화할 수 있을까?

=> 조금 더 깊게 생각해보겠습니다. 핀볼의 방향이 반대로 바뀌면, 다시 왔던 길을 그대로 돌아서 갑니다. 이 말은 곧 무조건 시작지점으로 돌아가게 되어있고, 부딪혔던 벽도 다시 부딪히게 된다는 뜻입니다. 따라서 핀볼의 방향이 반대로 될 때, 점수는 (지금까지 점수 x 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
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
#include <iostream>
#include <string.h>
#include <vector>
#include <algorithm>
 
using namespace std;
 
struct pos {
    int x, y;
};
 
int dx[] = { -1,0,1,0 };
int dy[] = { 0,1,0,-1 };
 
int block[6][4= { //-1은 왔던 방향의 반대로 가는 경우
    {0,0,0,0},
    {-1,-1,1,0},
    {1,-1,-1,2},
    {3,2,-1,-1},
    {-1,0,3,-1},
    {-1,-1,-1,-1}
};
 
int N;
int map[102][102];
vector<pos> wormhole[11];
int answer;
 
bool isStart(pos p, pos start) {
    if (p.x == start.x && p.y == start.y) return true;
    return false;
}
 
bool isBlackhole(pos p) {
    if (map[p.x][p.y] == -1return true;
    return false;
}
 
bool isBlock(pos p) {
    if (1 <= map[p.x][p.y] && map[p.x][p.y] <= 5return true;
    return false;
}
 
bool isEmptySpace(pos p) {
    if (map[p.x][p.y] == 0return true;
    return false;
}
 
bool isWormhole(pos p) {
    if (6 <= map[p.x][p.y] && map[p.x][p.y] <= 10return true;
    return false;
}
 
pos getPairWormhole(int num, pos p) {
    if (wormhole[num][0].x == p.x && wormhole[num][0].y == p.y) return wormhole[num][1];
    else return wormhole[num][0];
}
 
int simulation(pos start, int startDir) {
    int score = 0;
    pos p = start; 
    int dir = startDir;
    while (true) {
        p.x += dx[dir], p.y += dy[dir];
        //시작지점 or 블랙홀
        if (isStart(p, start) || isBlackhole(p)) break;
        //블록
        else if (isBlock(p)) {
            int blockNum = map[p.x][p.y];
            dir = block[blockNum][dir];
            if (dir == -1) {
                score = (score * 2+ 1;
                break;
            }
            else score++;
        }
        //웜홀
        else if (isWormhole(p)) {
            p = getPairWormhole(map[p.x][p.y], p);
        }
    }
    return score;
}
 
void solution() {
    for (int i = 1; i <= N; i++) {
        for (int j = 1; j <= N; j++) {
            if (isEmptySpace({ i,j })) {
                for (int k = 0; k < 4; k++) {
                    answer = max(answer, simulation({ i, j }, k));
                }
            }
        }
    }
}
 
int main() {
    int test_case;
    int T;
    cin >> T;
    for (test_case = 1; test_case <= T; test_case++) {
        //초기화
        N = 0;
        memset(map, 0sizeof(map));
        for (int i = 6; i <= 10; i++) wormhole[i].clear();
        answer = 0;
 
        //입력
        cin >> N;
        for (int i = 1; i <= N; i++) {
            map[i][0= 5, map[i][N + 1= 5, map[0][i] = 5, map[N + 1][i] = 5;
            for (int j = 1; j <= N; j++) {
                cin >> map[i][j];
                if (isWormhole({ i,j })) wormhole[map[i][j]].push_back({ i,j });
            }
        }
 
        //해법
        solution();
 
        //출력
        cout << "#" << test_case << " " << answer << "\n";
    }
 
    //종료
    return 0;
}

 

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

'알고리즘 문제풀이 > SWEA' 카테고리의 다른 글

[C++] SWEA 1227 - 미로2  (0) 2022.12.24
[C++] SWEA 1225 - 암호생성기  (0) 2022.12.24
[C++] SWEA 1226 - 미로1  (0) 2022.12.21
[C++] SWEA 1224 - 계산기3  (0) 2022.12.21
[C++] SWEA 1219 - 길찾기(2)  (0) 2022.12.21

https://www.acmicpc.net/problem/17471

 

17471번: 게리맨더링

선거구를 [1, 4], [2, 3, 5, 6]으로 나누면 각 선거구의 인구는 9, 8이 된다. 인구 차이는 1이고, 이 값보다 더 작은 값으로 선거구를 나눌 수는 없다.

www.acmicpc.net

<풀이>

1. 구역의 정보를 입력받는다.

2. 조건에 맞게 구역들을 두 개의 선거구에 포함시키고, 두 선거구간 인구 차이의 최솟값을 출력한다.

 

<해법>

1. 알고리즘 선택하기

=> 모든 경우를 다 해보아야합니다. 따라서, 저는 브루트포스를 선택하였습니다. 하지만, 여기서 한번 멈칫했습니다. 구역들은 한 선거구가 되려면 모두 연결되어있어야합니다. 그러면 이 조건을 (1)구역들을 뽑을 때 검사해야하나 (2)구역을 다 나눠놓고 검사해도되나 망설여집니다. 저는 시간복잡도를 따져보았습니다. 구역은 10개 이하로 주어지므로, 연산횟수는 1024회 입니다. 따라서, 저는 (2)를 선택했습니다.

 

2. 구역들이 모두 연결되어있는지 확인하기

=> 구역들을 두 개로 나누었다면, 같은 선거구의 구역들이 모두 연결되어있는지 확인해야 합니다. 저는 BFS를 이용해서 조건을 확인했습니다.

 

3. 구현 전 총정리

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

(1) 브루트포스 중 DFS를 선택하고, 재귀함수로 구현한다. 구역들을 두 선거구로 나누는 모든 경우를 해본다.

(2) 두 선거구로 나누었으면, ①두 선거구에 구역들이 하나라도 포함되어있는지 확인하고, ②같은 선거구에 있는 구역들이 모두 연결되어있는지 확인합니다. 

(3) 두 선거구간 인구차이의 최소를 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
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
#include <iostream>
#include <string.h>
#include <queue>
#include <vector>
#define INF 987654321
 
using namespace std;
 
int N;
int people[11];
bool edge[11][11];
vector<int> a, b;
int answer;
 
bool isLinked(vector<int> sector) {
    queue<int> q;
    bool visited[11];
    memset(visited, falsesizeof(visited));
 
    int start = sector.front();
    q.push(start);
    visited[start] = true;
    while (!q.empty()) {
        int cur = q.front();
        q.pop();
        for (int i = 0; i < sector.size(); i++) {
            int nxt = sector[i];
            if (!visited[nxt] && edge[cur][nxt]) {
                q.push(nxt);
                visited[nxt] = true;
            }
        }
    }
 
    for (int i = 0; i < sector.size(); i++) {
        int node = sector[i];
        if (!visited[node]) return false;
    }
    return true;
}
 
int divideSector(int idx, int sumA, int sumB) {
    if (idx > N) {
        if (!sumA || !sumB) return INF;
        else {
            if (isLinked(a) && isLinked(b)) return abs(sumA - sumB);
            else return INF;
        }
    }
 
    int ret = INF;
    //a구역에 넣는 경우
    a.push_back(idx);
    ret = min(ret, divideSector(idx + 1, sumA + people[idx], sumB));
    a.pop_back();
    //b구역에 넣는 경우
    b.push_back(idx);
    ret = min(ret, divideSector(idx + 1, sumA, sumB + people[idx]));
    b.pop_back();
    return ret;
}
 
void solution() {
    answer = divideSector(100);
    answer = (answer == INF) ? -1 : answer;
}
 
int main() {
    //초기화
    N = 0;
    memset(people, 0sizeof(people));
    memset(edge, falsesizeof(edge));
    answer = 0;
 
    //입력
    cin >> N;
    for (int i = 1; i <= N; i++) {
        cin >> people[i];
    }
    for (int i = 1; i <= N; i++) {
        int cnt = 0;
        cin >> cnt;
        for (int j = 0; j < cnt; j++) {
            int node = 0;
            cin >> node;
            edge[i][node] = true;
        }
    }
 
    //해법
    solution();
 
    //출력
    cout << answer << "\n";
 
    //종료
    return 0;
}

 

DFS와 BFS에 대해 알아볼 수 있는 문제였습니다.

https://www.acmicpc.net/problem/17406

 

17406번: 배열 돌리기 4

크기가 N×M 크기인 배열 A가 있을때, 배열 A의 값은 각 행에 있는 모든 수의 합 중 최솟값을 의미한다. 배열 A가 아래와 같은 경우 1행의 합은 6, 2행의 합은 4, 3행의 합은 15이다. 따라서, 배열 A의

www.acmicpc.net

<풀이>

1. 배열 정보와 회전 정보를 입력받는다.

2. 회전순서를 정해서 배열을 돌리고, 얻을 수 있는 배열의 최솟값을 출력한다.

 

<해법>

1. 알고리즘 선택하기

=> 회전 순서에 따라 배열이 다 달라지므로, 모든 경우의 회전순서를 다 만들어서 시뮬레이션을 진행해야합니다. 따라서, 저는 브루트포스를 선택하였습니다.

 

2. 시뮬레이션 구현하기

=> 회전순서를 정했으면, 시뮬레이션을 진행해야합니다. 시뮬레이션의 핵심은 문제의 조건에 맞는 정확한 구현입니다. 저는 배열을 돌릴때 Deque을 사용했습니다.

 

3. 구현 전 총정리

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

(1) 브루트포스 중 DFS를 선택하고, 재귀함수로 구현한다. 모든 경우의 회전순서를 만든다.

(2) 회전순서를 정했으면, 시뮬레이션을 진행한다.(배열을 돌릴 때 덱을 사용한다)

(3) 배열의 최솟값을 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
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
#include <iostream>
#include <string.h>
#include <algorithm>
#include <deque>
#define INF 987654321
 
using namespace std;
 
int N, M, K;
int A[51][51];
int A_copy[51][51];
int R[6], C[6], S[6];
int order[6];
bool visited[6];
int answer;
 
void rotate(int x1, int y1, int x2, int y2) {
    deque<int> dq;
    //숫자 담기
    for (int i = y1; i < y2; i++) dq.push_back(A_copy[x1][i]);
    for (int i = x1; i < x2; i++) dq.push_back(A_copy[i][y2]);
    for (int i = y2; i > y1; i--) dq.push_back(A_copy[x2][i]);
    for (int i = x2; i > x1; i--) dq.push_back(A_copy[i][y1]);
    //순서 바꾸기
    dq.push_front(dq.back());
    dq.pop_back();
    //숫자 순서대로 놓기
    for (int i = y1; i < y2; i++) {
        A_copy[x1][i] = dq.front(); dq.pop_front();
    }
    for (int i = x1; i < x2; i++) {
        A_copy[i][y2] = dq.front(); dq.pop_front();
    }
    for (int i = y2; i > y1; i--) {
        A_copy[x2][i] = dq.front(); dq.pop_front();
    }
    for (int i = x2; i > x1; i--) {
        A_copy[i][y1] = dq.front(); dq.pop_front();
    }
}
 
void copyA() {
    for (int i = 1; i <= N; i++) {
        for (int j = 1; j <= M; j++) {
            A_copy[i][j] = A[i][j];
        }
    }
}
 
void simulation() {
    //1. 맵 복사
    copyA();
    //2. 순서에 따라 배열 돌리기
    for (int i = 0; i < K; i++) {
        int o = order[i];
        int r = R[o], c = C[o], s = S[o];
        for (int j = 0; j < s; j++) {
            rotate(r - s + j, c - s + j, r + s - j, c + s - j);
        }
    }
}
 
int rowSum(int col) {
    int output = 0;
    for (int i = 1; i <= M; i++) {
        output += A_copy[col][i];
    }
    return output;
}
 
int minRowSum() {
    int output = INF;
    for (int i = 1; i <= N; i++) {
        output = min(output, rowSum(i));
    }
    return output;
}
 
int selectOperOrder(int cnt) {
    if (cnt == K) {
        simulation();
        return minRowSum();
    }
 
    int ret = INF;
    for (int i = 0; i < K; i++) {
        if (!visited[i]) {
            visited[i] = true;
            order[cnt] = i;
            ret = min(ret, selectOperOrder(cnt + 1));
            visited[i] = false;
        }
    }
    return ret;
}
 
void solution() {
    answer = selectOperOrder(0);
}
 
int main() {
    //초기화
    N = 0, M = 0, K = 0;
    memset(A, 0sizeof(A));
    memset(R, 0sizeof(R));
    memset(C, 0sizeof(C));
    memset(S, 0sizeof(S));
    memset(order, 0sizeof(order));
    memset(visited, falsesizeof(visited));
    answer = 0;
 
    //입력
    cin >> N >> M >> K;
    for (int i = 1; i <= N; i++) {
        for (int j = 1; j <= M; j++) {
            cin >> A[i][j];
        }
    }
    for (int i = 0; i < K; i++) {
        cin >> R[i] >> C[i] >> S[i];
    }
 
    //해법
    solution();
 
    //출력
    cout << answer << "\n";
 
    //종료
    return 0;
}

 

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

https://www.acmicpc.net/problem/17281

 

17281번: ⚾

⚾는 9명으로 이루어진 두 팀이 공격과 수비를 번갈아 하는 게임이다. 하나의 이닝은 공격과 수비로 이루어져 있고, 총 N이닝 동안 게임을 진행해야 한다. 한 이닝에 3아웃이 발생하면 이닝이 종

www.acmicpc.net

<풀이>

1. 이닝 별 선수 타격을 입력받는다.

2. 1번 타자를 4번으로 고정했을 때, 타순을 잘 정해서 팀이 얻을 수 있는 최대 점수를 출력한다.

 

<해법>

1. 알고리즘 선택하기

=> 1번 타자를 4번으로 고정하고, 모든 경우의 타선을 다 해보아야합니다. 따라서, 저는 브루트포스를 선택하였습니다.

 

2. 시뮬레이션 구현하기

=> 타순이 정해졌으면, 시뮬레이션을 진행해야합니다. 시뮬레이션의 핵심은 문제의 조건에 맞는 정확한 구현입니다. 저는 시뮬레이션 구현에서 타선을 Queue로 구현하였습니다.

 

3. 구현 전 총정리

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

(1) 브루트포스 중 DFS를 선택하고, 재귀함수로 모든 경우의 타선을 만들어본다.

(2) 타선이 완성됐으면, 시뮬레이션을 진행한다.(타선은 큐로 구현한다)

(3) 얻을 수 있는 최대 점수를 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
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
#include <iostream>
#include <string.h>
#include <algorithm>
#include <queue>
 
using namespace std;
 
int N;
int record[50][9];
int order[9];
bool visited[9];
int answer;
 
int getPoint(int hit, bool base[]) {
    int output = 0;
 
    base[0= true;
    for (int i = 3; i >= 0; i--) {
        if (base[i]) { //주자가 있을 경우
            if (i + hit > 3) {  //홈인
                base[i] = false;
                output++;
            }
            else { //진루
                base[i] = false;
                base[i + hit] = true;
            }
        }
    }
    return output;
}
 
int simulation() {
    int output = 0;    
    int inning = 0;
    queue<int> q; //타선을 큐로 구현
    for (int i = 0; i < 9; i++) {
        q.push(order[i]);
    }
    
    while (inning < N) {
        bool base[4];
        memset(base, falsesizeof(base));
        int out = 0;
 
        while (out != 3) {
            int hitter = q.front();
            q.pop();
            
            int hit = record[inning][hitter];
            if (hit == 0) out++;
            else output += getPoint(hit, base);
            
            q.push(hitter);
        }
        inning++;
    }
 
    return output;
}
 
int ordering(int idx) {
    if (idx == 9) {
        return simulation();
    }
 
    int ret = 0;
    if (idx == 3) ret = ordering(idx + 1);
    else {
        for (int i = 0; i < 9; i++) {
            if (!visited[i]) {
                order[idx] = i;
                visited[i] = true;
                ret = max(ret, ordering(idx + 1));
                visited[i] = false;
            }
        }
    }
    return ret;
}
 
void solution() {
    visited[0= true;
    order[3= 0;
    answer = ordering(0);
}
 
int main() {
    //초기화
    N = 0;
    memset(record, 0sizeof(record));
    memset(order, 0sizeof(order));
    memset(visited, falsesizeof(visited));
    answer = 0;
 
    //입력
    cin >> N;
    for (int i = 0; i < N; i++) {
        for (int j = 0; j < 9; j++) {
            cin >> record[i][j];
        }
    }
 
    //해법
    solution();
 
    //출력
    cout << answer << "\n";
 
    //종료
    return 0;
}

 

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

https://www.acmicpc.net/problem/17136

 

17136번: 색종이 붙이기

<그림 1>과 같이 정사각형 모양을 한 다섯 종류의 색종이가 있다. 색종이의 크기는 1×1, 2×2, 3×3, 4×4, 5×5로 총 다섯 종류가 있으며, 각 종류의 색종이는 5개씩 가지고 있다. <그림 1> 색종이를 크

www.acmicpc.net

<풀이>

1. 종이 상태를 입력받는다.

2. 종이의 모든 1을 덮는데 필요한 색종이의 최소 갯수를 출력한다.

 

<해법>

1. 알고리즘 선택하기

=> 큰 색종이부터 먼저 붙이는 그리디한 방식으로 불가능합니다. 왜냐하면, 6x6을 최소로 덮는 방법은 5x5를 사용하는 것이 아닌, 3x3을 사용하는 것이기 때문입니다. 그러므로, 색종이를 붙여야하는 곳에 5가지 색종이를 모두 붙여보아야 합니다. 따라서, 저는 브루트포스를 선택했습니다.

 

2. 구현 방법 떠올리기

=> 아래는 구현 전에 저가 미리 생각한 것입니다.

(1) 브루트포스 중 DFS를 선택하고, 재귀함수로 구현한다.

(2) 종이의 좌표를 순차적으로 탐색하면서, 1인 부분에 5가지 색종이를 모두 덮어본다.

(3) 종이를 모두 탐색하면 함수는 종료된다.

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

 

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
#include <iostream>
#include <string.h>
#include <algorithm>
#define INF 987654321
 
using namespace std;
 
int N;
int map[10][10];
bool covered[10][10];
int cnt[6];
int answer;
 
bool canCover(int x, int y, int length) {
    //종이를 다 쓴 경우
    if (cnt[length] == 5return false;
    //범위 벗어난 경우
    if (x + length > N || y + length > N) return false;
    //0을 덮거나, 이미 덮여져 있는 경우
    for (int i = x; i < x + length; i++) {
        for (int j = y; j < y + length; j++) {
            if (map[i][j] == 0 || covered[i][j]) return false;
        }
    }
    return true;
}
 
void cover(int x, int y, int length, bool type) {
    for (int i = x; i < x + length; i++) {
        for (int j = y; j < y + length; j++) {
            covered[i][j] = type;
        }
    }
}
 
void getNxt(int& x, int& y) {
    if (y == 9) x++, y = 0;
    else y++;
}
 
int coverPaper(int x, int y, int sum) {
 
    if (x == N) return sum;
 
    int ret = INF;
    //색종이를 붙여야 하는 경우
    if (map[x][y] == 1 && !covered[x][y]) {
        for (int i = 1; i <= 5; i++) {
            if (canCover(x, y, i)) {
                cover(x, y, i, true);
                cnt[i]++;
                int nx = x, ny = y;
                getNxt(nx, ny);
                ret = min(ret, coverPaper(nx, ny, sum + 1));
                cover(x, y, i, false);
                cnt[i]--;
            }
        }
    }
    else {
        int nx = x, ny = y;
        getNxt(nx, ny);
        ret = coverPaper(nx, ny, sum);
    }
    return ret;
}
 
void solution() {
    answer = coverPaper(000);
    answer = (answer == INF) ? -1 : answer;
}
 
int main() {
    //초기화
    N = 10;
    memset(map, 0sizeof(map));
    memset(covered, falsesizeof(covered));
    memset(cnt, 0sizeof(cnt));
    answer = 0;
 
    //입력
    for (int i = 0; i < N; i++) {
        for (int j = 0; j < N; j++) {
            cin >> map[i][j];
        }
    }
 
    //해법
    solution();
 
    //출력
    cout << answer << "\n";
 
    //종료
    return 0;
}

 

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

https://www.acmicpc.net/problem/17135

 

17135번: 캐슬 디펜스

첫째 줄에 격자판 행의 수 N, 열의 수 M, 궁수의 공격 거리 제한 D가 주어진다. 둘째 줄부터 N개의 줄에는 격자판의 상태가 주어진다. 0은 빈 칸, 1은 적이 있는 칸이다.

www.acmicpc.net

<풀이>

1. 격자판을 입력받는다.

2. 궁수 3명을 배치해서, 죽일 수 있는 적의 최대 수를 출력한다.

 

<해법>

1. 알고리즘 선택하기

=> 궁수 3명을 배치할 수 있는 모든 경우를 해보아야 합니다. 따라서, 저는 브루트포스를 선택해서 궁수를 모든 경우에 배치해보았습니다.

 

2. 시뮬레이션 구현하기

=> 궁수 3명을 배치한 이후에는 시뮬레이션을 진행해야합니다. 시뮬레이션의 핵심은 문제의 조건에 맞게 정확하게 구현하는 것입니다. 구현방법은 아래의 코드를 참조해주세요.

 

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
#include <iostream>
#include <string.h>
#include <algorithm>
#include <vector>
 
using namespace std;
 
struct pos {
    int x, y;
};
 
struct enemy {
    pos p;
    int dist;
};
 
int N, M, D;
int map[15][15];
int map_copy[15][15];
int archer[3];
int answer;
 
bool cmp(enemy e1, enemy e2) {
    if (e1.dist == e2.dist) {
        if (e1.p.y == e2.p.y) return e1.p.x < e2.p.x;
        return e1.p.y < e2.p.y;
    }
    return e1.dist < e2.dist;
}
 
int getDist(int x1, int y1, int x2, int y2) {
    return abs(x1 - x2) + abs(y1 - y2);
}
 
bool isNoEnemy(int col) {
    for (int i = 0; i < col; i++) {
        for (int j = 0; j < M; j++) {
            if (map_copy[i][j] == 1return false;
        }
    }
    return true;
}
 
void copyMap() {
    for (int i = 0; i < N; i++) {
        for (int j = 0; j < M; j++) {
            map_copy[i][j] = map[i][j];
        }
    }
}
 
int countDeadEnemy() {
 
    int output = 0;
 
    //1. 시뮬레이션 맵 카피
    copyMap();
    int archerCol = N;
    //2. 시뮬레이션
    while (archerCol >= 1) {
        if (isNoEnemy(archerCol)) break;
        //(1) 모든 궁수마다 죽일 적 선택하기
        vector<pos> kill;
        for (int i = 0; i < 3; i++) {
            vector<enemy> v;
            //적 담기
            for (int j = 0; j < archerCol; j++) {
                for (int k = 0; k < M; k++) {
                    if (map_copy[j][k] == 1) {
                        int dist = getDist(archerCol, archer[i], j, k);
                        if (dist <= D) v.push_back({ {j,k},dist });
                    }
                }
            }
            //정렬
            sort(v.begin(), v.end(), cmp);
            //죽일 적 1명 담기
            if (!v.empty()) kill.push_back(v.front().p);
        }
        //(2) 적 죽이기
        for (auto& k : kill) {
            if (map_copy[k.x][k.y] == 1) {
                map_copy[k.x][k.y] = 0;
                output++;
            }
        }
        //(3) 궁수 라인 올리기
        archerCol--;
    }
 
    return output;
}
 
int selectArcherPos(int idx, int cnt) {
 
    if (cnt == 3return countDeadEnemy();
 
    int ret = 0;
    for (int i = idx; i < M; i++) {
        archer[cnt] = i;
        ret = max(ret, selectArcherPos(i + 1, cnt + 1));
    }
    return ret;
}
 
void solution() {
    answer = selectArcherPos(00);
}
 
int main() {
    //초기화
    N = 0, M = 0, D = 0;
    memset(map, 0sizeof(map));
    memset(map_copy, 0sizeof(map_copy));
    memset(archer, 0sizeof(archer));
    answer = 0;
 
    //입력
    cin >> N >> M >> D;
    for (int i = 0; i < N; i++) {
        for (int j = 0; j < M; j++) {
            cin >> map[i][j];
        }
    }
 
    //해법
    solution();
 
    //출력
    cout << answer << "\n";
 
    //종료
    return 0;
}

 

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

+ Recent posts