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

 

17090번: 미로 탈출하기

크기가 N×M인 미로가 있고, 미로는 크기가 1×1인 칸으로 나누어져 있다. 미로의 각 칸에는 문자가 하나 적혀있는데, 적혀있는 문자에 따라서 다른 칸으로 이동할 수 있다. 어떤 칸(r, c)에 적힌 문자가 U인 경우에는 (r-1, c)로 이동해야 한다. R인 경우에는 (r, c+1)로 이동해야 한다. D인 경우에는 (r+1, c)로 이동해야 한다. L인 경우에는 (r, c-1)로 이동해야 한다. 미로에서 탈출 가능한 칸의 수를 계산해보자. 탈출 가능한

www.acmicpc.net

각 칸은 방향이 정해져 있기 때문에 하나의 방향으로만 이동하여 경로는 정해져 있게 된다. 즉 탈출하는 경로를 찾으면 해당 경로에 해당하는 칸들은 모두 탈출 가능하다. 마찬가지로 탈출할 수 없는 경로가 있다면 해당 칸들은 모두 탈출 불가능하다. 참고로 경로를 따로 저장해놨다가 나중에 다시 한번에 체크해주는 방식으로 구현하면 시간 초과가 발생하므로 DFS로 구현해야 한다.

 

 

3 3

DDR

DLU

LLL

문제의 2번째 예제로 예를 들어보면 첫 번째 칸(0, 0)인 D에서 출발하면 정해진 방향에 따라 D-D-L의 경로로 탈출이 가능하다. 그러면 당연히 (1, 0)인 D에서도 D-L 로도 탈출 가능하고, (2, 0) L에서도 탈출 가능하다. 정확히는 L에서 탈출이 가능한 것이고 위의 D에서는 L로 향하게 방향이 되어있기 때문에 마찬가지로 탈출이 가능하다.

 

 

3 3

DDR

DLU

LLL

다음에 (0, 1) 칸인 위의 파란색 D에서 출발하면 아래로 내려와 L에 도착하고 L에서는 (1, 0)인 D에 도착한다. 그러면 위에서 이미 탈출 가능하다고 확인되었으므로 D에서 L까지 오는 경로는 모두 탈출 가능하다고 판단이 가능하다.

 

 

3 4

RRDD

RRDR

DULU

문제의 예제 입력 4번을 마지막 예시로 들어보면 처음 (0, 0)인 R에서 출발했을 때, R-R-D-D-L-U-R까지 왔다가 다시 D로 와서 사이클이 발생하기 때문에 탈출이 불가능하다. 이때의 각 경로에서는 탈출이 불가능하다는 것을 저장해놓으면 다음 (0,1), (0,2) 등의 빨간 경로에서는 다시 탐색할 필요가 없다.

 

 

3 4

RRDD

RRDR

DULU

마찬가지로 (1, 0)에서 출발하면 미리 저장해놓은 탈출할 수 없는 경로인 (1, 1) R에 도착하므로 탈출할 수 없는 칸이라는 것을 금방 알 수 있다.

 

 

이제 구현을 위해서는 문자를 입력받을 map배열 이외에 두 가지 배열을 더 사용하였다.

x, y 칸에서 탈출이 가능한지 저장할 check배열과 매번 각 탐색에서 방문했는지 확인할 visit배열을 사용하였다.

check배열의 값이 1이면 탈출 가능 / 2이면 탈출 불가능 / 0이면 아직 확인 안 됨으로 해놓고 구현했다.

 

 

또한, 이동한 경로를 저장하기 위해서 DFS를 사용하였다. 이동할 수 있는 칸까지 계속 이동하다가 탈출에 성공하거나 사이클이 발견돼서 실패하는 칸을 발견할 때까지 쭉쭉 탐색을 한다. 탈출에 성공했다면 true값을 리턴하고 실패했다면 false값을 리턴한다. DFS로 리턴 받은 값이 true이면 현재 칸에서 탈출이 가능하다는 뜻이므로 check배열 값을 1로 바꿔주고 탐색에서 돌아왔으므로 visit배열은 false로 바꿔준다. 반대로 false를 리턴 받았다면 check배열의 값을 2로 해주고 visit배열은 마찬가지로 false로 바꿔준다.

 

 

다시 정리를 해보면 복잡해 보이긴 하지만 아래와 같다.

 

  1. 입력을 받는다.
  2. 모든 칸을 검사하여 탈출할 수 있는지 검사한다.
    • 이미 탈출 불가능한 칸으로 확인되었다면 DFS탐색을 하지 않고 다음 칸으로 넘어간다.
    • 이미 탈출 가능한 칸으로 확인되었다면 정답 카운트를 증가시키고 DFS탐색을 하지 않는다. 마찬가지로 다음 칸으로 넘어간다.
    • 확인되지 않은 칸이라면 DFS로 이동해서 경로를 확인하고 탈출할 수 있다면 정답 카운트를 증가시킨다.
  3. 탈출할 수 있는지 검사하기 위해 DFS를 진행한다.
    •  해당 칸에 적혀 있는 문자에 따라 좌표를 이동한다.
    • 범위를 벗어나면 탈출에 성공한 것이므로 해당 좌표의 check배열 값을 1로 바꿔주고 true를 리턴한다.
    • 방문한 칸의 check배열을 값이 1이라면 이미 탈출 가능한 칸이라고 확인됐으므로 check배열의 값을 1로 바꾸고 true를 리턴한다.
    • visit배열을 확인하여 이번 탐색에서 방문했던 칸을 다시 방문하거나(cycle 발생) check배열을 확인했을 때 값이 2로 이미 탈출 불가능한 칸이라고 확인됐으면 check배열의 값을 2로 해주고 false를 리턴한다.
    • 위의 경우들이 아닌 이동한 칸에서 이동을 계속하는 경우에는 DFS탐색을 계속해주면 된다. visit배열을 true값으로 바꿔놓고 재귀 호출로 다음 칸으로 이동한고, 재귀 호출에서 돌아오면 visit배열을 다시 false값으로 바꿔준다. 탐색 결과에 따라 check배열의 값을 바꿔주면 되는데, 결과는 위의 3가지 상황 중에서 true나 false를 리턴 받은 값이다. 이전의 호출 함수에게도 이 결과를 그대로 리턴해줘서 현재 경로의 해당하는 모든 칸에 탈출이 가능한지 체크하도록 해준다.

 

 

DFS의 기본 개념을 알고 있다면 어렵지 않은 문제다.

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
#include <iostream>
using namespace std;
 
int n, m;
char map[501][501];
bool visit[501][501];
int check[501][501];
 
 
bool solve(int r, int c) {
    //이동 전 위치 저장
    int x = r;
    int y = c;
        
    char dir = map[r][c];
 
    if (dir == 'U') r -= 1;
    else if (dir == 'R') c += 1;
    else if (dir == 'D') r += 1;
    else c -= 1;
 
    if (r < 0 || c < 0 || r >= n || c >= m) {
        //탈출
        check[x][y] = 1;
        return true;
    } else if (check[r][c] == 1) {
        //탈출할 수 있는 경로
        check[x][y] = 1;
        return true;
    } else if (visit[r][c] || check[r][c] == 2) {
        //방문했던 곳으로 다시 돌아오거나(cycle) || 탈출할 수 없는 경로
        check[x][y] = 2;
        return false;
    }
    else {
        visit[r][c] = true;
        bool flag = solve(r, c);
        visit[r][c] = false;
 
        if (flag) check[r][c] = 1;
        else check[r][c] = 2;
 
        return flag;
    }
}
 
 
int main() {
    ios_base::sync_with_stdio(false);
    cin.tie(0);
 
    cin >> n >> m;
    for (int i = 0; i < n; i++cin >> map[i];
 
    int ans = 0;
    for (int i = 0; i < n; i++) {
        for (int j = 0; j < m; j++) {
            if (check[i][j] == 2continue//탈출불가능
            if (check[i][j] == 1) ans++//탈출가능
            else {
                if (solve(i, j)) ans++;
            }
        }
    }
 
    cout << ans << '\n';
 
    return 0;
}
Colored by Color Scripter
 

 

 

 

'BOJ' 카테고리의 다른 글

[BOJ] 16401. 과자 나눠주기  (2) 2020.04.29
[BOJ] 2174. 로봇 시뮬레이션  (0) 2020.04.28
[BOJ] 10711. 모래성  (0) 2020.04.27
[BOJ] 2866. 문자열 잘라내기  (0) 2020.04.21
[BOJ] 5427. 불  (0) 2020.04.19

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

 

16401번: 과자 나눠주기

첫째 줄에  조카의 수 M (1 ≤ M ≤ 1,000,000), 과자의 수 N (1 ≤ N ≤ 1,000,000)이 주어진다. 둘째 줄에 과자 N개의 길이 L1, L2, ..., LN이 공백으로 구분되어 주어진다. 과자의 길이는 (1 ≤ L1, L2, ..., LN ≤ 1,000,000,000) 를 만족한다.

www.acmicpc.net

이분 탐색으로 조카들에게 나눠줄 수 있는 과자의 최대 길이를 찾아줘야 한다.

이분 탐색의 처음 범위는 1개를 나눠줄 수 있는 경우부터 가지고 있는 과자 중 가장 큰 과자의 길이까지가 된다.

 

 

3 10

1 2 3 4 5 6 7 8 9 10

문제의 첫 번째 예시처럼 다음과 위와 같이 막대과자를 가지고 있다면 1에서 10까지의 범위를 탐색해준다.

 

4 3

10 10 15

2번째 예제에서의 범위는 1부터 15까지이다.

 

 

문제의 예시에서는 막대과자의 길이가 모두 정렬된 순서로 되어있지만 히든 케이스에서는 정렬되어 있지 않을 수도 있으므로 입력받은 막대과자의 길이 배열을 정렬해놓고 탐색을 시작한다.

 

 

이분 탐색으로 매번 mid값을 구해서 mid길이만큼을 조카들에게 나눠줄 수 있는지 검사한다.

나눠줄 수 있다면 더 큰 길이로 나눠줄 수 있는지 mid값보다 큰 범위를 탐색하고, 나눠줄 수 없다면 더 작은 길이를 찾아야 한다.

 

 

나눠줄 수 있는지 검사할 때에는 길이가 긴 막대과자들부터 mid길이만큼을 나눌 수 있는지 확인한다. 배열을 앞에서 정렬해놓았으므로 배열의 뒤쪽부터 검사해주면 된다. mid길이만큼의 막대과자가 조카의 수이상 나온다면 mid 길이만큼씩 나누어줄 수 있다.

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
#include <iostream>
#include <algorithm>
using namespace std;
 
int M, N;
int ans;
int arr[1000001];
 
bool check(int len) {
    int tmp;
    int cnt = 0;
 
    //배열의 맨 뒤(큰 값)부터 검사
    for (int i = N - 1; i >= 0; i--) {
        if (arr[i] < len) break;
 
        tmp = arr[i];
        while (tmp >= len) {
            cnt++;
            tmp -= len;
        }
        //len만큼의 길이가 조카의 수만큼 나온다면 바로 true를 리턴
        if (cnt >= M) return true;
    }
 
    if (cnt >= M) return true;
    else return false;
}
 
 
void binarySearch(int left, int right) {
    if (left > right) return;
 
    int mid = (left + right) / 2;
    //막대 과자 길이가 mid일때, 조카들에게 나눠줄 수 있는지 검사
    bool res = check(mid);
    if (res) {
        //나눠줄 수 있다면 일단 길이를 저장해놓고, 더 큰 길이를 나눠줄 수 있는지 탐색
        if (ans < mid) ans = mid;
        binarySearch(mid + 1, right);
    }
    else {
        //mid 길이 만큼씩 나눠줄 수 없다면 더 작은 길이를 나눠줘야한다.
        binarySearch(left, mid - 1);
    }
}
 
 
int main() {
    ios_base::sync_with_stdio(false);
    cin.tie(0);
 
    cin >> M >> N;
 
    for (int i = 0; i < N; i++cin >> arr[i];
    sort(arr, arr + N);
 
    ans = 0;
    binarySearch(1, arr[N - 1]);
 
    cout << ans << '\n';
 
    return 0;
}
Colored by Color Scripter
 

 

'BOJ' 카테고리의 다른 글

[BOJ] 17090. 미로 탈출하기  (0) 2020.05.01
[BOJ] 2174. 로봇 시뮬레이션  (0) 2020.04.28
[BOJ] 10711. 모래성  (0) 2020.04.27
[BOJ] 2866. 문자열 잘라내기  (0) 2020.04.21
[BOJ] 5427. 불  (0) 2020.04.19

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

 

2174번: 로봇 시뮬레이션

문제 가로 A(1≤A≤100), 세로 B(1≤B≤100) 크기의 땅이 있다. 이 땅 위에 로봇들이 N(1≤N≤100)개 있다. 로봇들의 초기 위치는 x좌표와 y좌표로 나타난다. 위의 그림에서 보듯 x좌표는 왼쪽부터, y좌표는 아래쪽부터 순서가 매겨진다. 또한 각 로봇은 맨 처음에 NWES 중 하나의 방향을 향해 서 있다. 초기에 서 있는 로봇들의 위치는 서로 다르다. 이러한 로봇들에 M(1≤M≤100)개의 명령을 내리려고 한다. 각각의 명령은 순차적으로

www.acmicpc.net

이 문제의 정답률이 낮은 것은 방향이 헷갈려서인 것 같다. 우선 1번 인덱스가 왼쪽 아래부터 시작하고, 입력받는 x, y 도 반대이다. 문제에서 주어지는 아래 그림에서 오른쪽 위에 있는 로봇의 좌표는 (5, 4)인 것에 주의해야 한다.

 

 

 

 

 

 

위의 그림을 아래 그림처럼 시계방향으로 90도 돌려서 생각하면 쉽게 풀 수 있다. (x <= A, y <= B)

일반적으로 사용하는 2차원 배열의 모양이므로 (x, y)를 입력받은 그대로 사용할 수 있다.

대신에 방향도 같이 90도 돌려야 하므로 W면 N방향으로/ N이면 E방향으로/ E면 S방향으로/ S면 W방향으로 바꿔준다.

 

로봇 정보는 현재 위치정보와 방향 정보를 구조체에 넣어서 관리한다. 로봇들은 처음에 입력받은 순서대로 번호를 가지므로 로봇 정보를 가지는 구조체를 벡터에 순차적으로 넣어놓는다. 벡터의 i번 인덱스 위치에 들어있는 로봇 정보가 i번 로봇의 정보이다. 1번 로봇부터 사용하기 위해 벡터에 0번 인덱스 위치에 아무 값을 넣어주고 시작하였다.

 

 

로봇이 이동하는 위치에 다른 로봇에 이미 있는지 검사하기 위해 2차원 int 배열에 로봇의 번호를 저장해놓는다.

배열의 (x, y)에 있는 로봇의 번호를 저장하고, 로봇이 없다면 초기값인 0이다.

예를 들어 로봇 2번이 (5, 4)에 있다면  check[5][4] = 2 이다.

이동후에는 기존 위치의 값을 다시 0으로 만들어주고 새로 이동한 곳에 로봇 번호를 다시 저장한다.

 

 

이후에는 입력받은 로봇 번호, 명령, 반복 횟수를 이용하여 시뮬레이션해주면 된다. 벽에 부딪히거나 다른 로봇에 부딪히는 경우는 F명령으로 이동했을 때만 발생하므로 F명령일 때만 검사해주면 된다.

 

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
#include <iostream>
#include <vector>
using namespace std;
 
int A, B, N, M;
 
int check[102][102];
 
//북, 동, 남, 서
int dx[] = { -1,0,1,0 };
int dy[] = { 0,1,0,-1 };
 
struct Robot {
    int x;
    int y;
    int dir;
    Robot(int a, int b, int c) {
        x = a;
        y = b;
        dir = c;
    }
};
 
vector<Robot> vt;
 
int main() {
    ios_base::sync_with_stdio(false);
    cin.tie(0);
 
    cin >> A >> B;
    cin >> N >> M;
 
    int x, y, dir;
    char d;
 
    //index 1부터 시작하기위해 아무 값 넣어줌
    vt.push_back(Robot(-1-1-1));
 
    for (int i = 1; i <= N; i++) {
        cin >> x >> y >> d;
 
        if (d == 'N'//N을 입력받으면 E방향으로 설정.
            dir = 1;
        else if (d == 'E'//E면 S방향으로 설정.
            dir = 2;
        else if (d == 'S'//S면 W방향으로 설정.
            dir = 3;
        else
            dir = 0//W면 N방향으로 설정
 
        vt.push_back(Robot(x, y, dir));
        check[x][y] = i;
    }
 
 
    int num;
    char order;
    int orderCnt;
 
    int crashWall = 0//벽에 부딪힌 로봇 번호를 저장
    int lastRobot = 0//다른 로봇에 부딪힌 현재 이동 로봇을 저장
    int crashRobot = 0//현재 이동중인 로봇이 부딪힌 다른 로봇의 번호를 저장
 
 
    while (M--) {
        cin >> num >> order >> orderCnt;
 
        //잘못된 명령이 발생해서 벽이나 로봇이 부딪힌 경우 넘어가서 입력만 계속 받는다.
        if (crashWall || crashRobot) continue;
 
        x = vt[num].x;
        y = vt[num].y;
        dir = vt[num].dir;
 
        //명령 반복 횟수만큼 반복
        while (orderCnt--) {
            if (order == 'L') {
                //왼쪽 90도 회전
                dir = (dir + 3) % 4;
                vt[num].dir = dir;
            }
            else if (order == 'R') {
                //오른쪽 90도 회전
                dir = (dir + 1) % 4;
                vt[num].dir = dir;
            }
            else if (order == 'F') {
                
                //이전 위치 비워준다.
                check[x][y] = 0;
 
                x = x + dx[dir];
                y = y + dy[dir];
 
                if (x < 1 || y < 1 || x > A || y > B) {
                    //벽에 부딪힘
                    crashWall = num;
                    break;
                }
                else if (check[x][y] != 0) {
                    //로봇에 부딪힘
                    lastRobot = num;
                    crashRobot = check[x][y];
                    break;
                }
                else {
                    //이동
                    vt[num].x = x;
                    vt[num].y = y;
                    check[x][y] = num;
                }
            }
 
        }
    }
 
 
    if (crashWall) cout << "Robot " << crashWall << " crashes into the wall" << '\n';
    else if (crashRobot) cout << "Robot " << lastRobot << " crashes into robot " << crashRobot << '\n';
    else cout << "OK" << '\n';
 
 
    return 0;
}
Colored by Color Scripter
 

'BOJ' 카테고리의 다른 글

[BOJ] 17090. 미로 탈출하기  (0) 2020.05.01
[BOJ] 16401. 과자 나눠주기  (2) 2020.04.29
[BOJ] 10711. 모래성  (0) 2020.04.27
[BOJ] 2866. 문자열 잘라내기  (0) 2020.04.21
[BOJ] 5427. 불  (0) 2020.04.19

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

 

10711번: 모래성

문제 명우와 친구들은 여름방학을 맞이하여 해변가에 놀러가기로 했다. 이번에 여행을 떠난 해수욕장의 이름은 ALPS(Awsome Land & Poor Sea)이다. 해변가에서 수영복을 입은 미녀들에게 관심이 많은 원철이와는 달리 명우는 해변가의 모래에 더 관심이 많다. 해변가의 모래는 무한한 것들을 만들 수 있는 가능성을 내포하고 있다. 또한 이렇게 만들어진 작품이 파도에 의해 사라지는 모습은, 마치 자신이 가장 빛날 수 있는 시간을 알고 스스로 아름답게

www.acmicpc.net

처음에 무너질 모래성을 매번 검사해서 없애주는 방식으로 구현했다가 시간 초과가 났다.

 

 

어차피 나중에 무너질 모래성들은 이전에 무너진 모래성들이 없어져서 영향을 받으므로 무너지는 모래성의 위치로부터

주변을 탐색하여 또 무너질 모래성이 있는지 찾아주고 다시 없애주는 방식으로 구현하면 문제를 해결할 수 있다.

 

 

 

  1. 입력을 받는다.
  2. 입력 받은 배열에서 튼튼함이 9보다 작은 모든 모래성을 검사하여 자기 격자 주변의 8방향에 모래성이 쌓여있지 않은 부분의 개수가 자기 모래성의 튼튼함보다 많거나 같은 경우 무너질 것이므로 큐에 넣어주고 배열에도 방문 표시를 해준다. 이때 다음에 무너져야 하는 모래성이 영향을 받으므로 바로 ' . '으로 바꿔주면 안 된다.
  3. 파도가 한 번 칠 때의 무너질 모래성들을 모두 큐에 넣었다면, ' . '으로 바꿔준다. 그리고 무너질 모래성 주변의 8방향을 다시 검사하여 또 무너질 모래성이 있다면 큐에 넣어준다. 이때 기존에 큐에 들어가 있던 모래성의 개수(큐 사이즈)만큼만 탐색을 진행하여, 탐색이 끝난다면 파도치는 횟수를 증가시킨다.
  4. 더 이상 무너질 모래성이 없어서 탐색이 끝난다면 파도치는 횟수를 출력하고 종료한다.

 

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
#include <iostream>
#include <queue>
using namespace std;
 
int h, w;
char map[1000][1000];
bool visit[1000][1000];
int dx[] = { -1,-1,-1,0,0,1,1,1 };
int dy[] = { -1,0,1,-1,1,-1,0,1 };
 
queue<pair<intint>> q;
 
int check(int x, int y) {
    int cnt = 0;
    int nx, ny;
 
    //자기 격자 주변의 8방향을 검사하여 모래성이 없다면 cnt증가
    for (int k = 0; k < 8; k++) {
        nx = x + dx[k];
        ny = y + dy[k];
        if (map[nx][ny] == '.') cnt++;
    }
 
    return cnt;
}
 
void solve() {
    queue<pair<intint> > tmp;
    int time = 0;
 
    int x, y;
 
    //더이상 무너질 모래성이 없을때까지 반복
    while (!q.empty()) {
        tmp = q;
 
        //이전에 무너질 모래성들을 없애준다.
        while (!tmp.empty()) {
            x = tmp.front().first;
            y = tmp.front().second;
            tmp.pop();
            map[x][y] = '.';
        }
 
        int qsize = q.size();
        while (qsize--) {
            x = q.front().first;
            y = q.front().second;
            q.pop();
 
            int nx, ny;
            for (int k = 0; k < 8; k++) {
                nx = x + dx[k];
                ny = y + dy[k];
 
                if (visit[nx][ny]) continue//이미 체크함
                if (map[nx][ny] == '.'continue//모래성이 아님
 
 
                //자기 모래성의 튼튼함보다 많거나 같은 경우 파도에 의해서 무너진다.
                if (check(nx, ny) >= (map[nx][ny] - '0')) {
                    q.push(make_pair(nx, ny));
                    visit[nx][ny] = true;
                }
            }
        }
        //시간 증가
        time++;
    }
 
    cout << time << '\n';
}
 
 
int main() {
    ios_base::sync_with_stdio(false);
    cin.tie(0);
 
    cin >> h >> w;
    for (int i = 0; i < h; i++cin >> map[i];
 
    int num, cnt;
    //무너질 모래의 위치를 큐에 넣는다.
    for (int i = 0; i < h; i++) {
        for (int j = 0; j < w; j++) {
            if (map[i][j] == '.'continue;
 
            num = map[i][j] - '0';
            if (num == 9continue//튼튼함이 9이면 무너지지 않으므로 넘어간다
 
            //(i, j)칸 주변 8방향에 모래성이 쌓여있지 않은 부분의 개수
            cnt = check(i, j);
 
            //자기 모래성의 튼튼함보다 많거나 같은 경우 파도에 의해서 무너진다.
            if (cnt >= num) {
                q.push(make_pair(i, j));
                visit[i][j] = true;
            }
        }
    }
 
 
    solve();
 
    return 0;
}
htColored by Color Scripter
 

 

 

'BOJ' 카테고리의 다른 글

[BOJ] 16401. 과자 나눠주기  (2) 2020.04.29
[BOJ] 2174. 로봇 시뮬레이션  (0) 2020.04.28
[BOJ] 2866. 문자열 잘라내기  (0) 2020.04.21
[BOJ] 5427. 불  (0) 2020.04.19
[BOJ] 18809. Gaaaaaaaaaarden  (0) 2020.03.23

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

 

2866번: 문자열 잘라내기

문제 R개의 행과 C개의 열로 이루어진 테이블이 입력으로 들어오게 됩니다. 이 테이블의 원소는 알파벳 소문자로 주어집니다. 각 테이블의 열을 위에서 아래로 읽어서 하나의 문자열을 만들 수 있습니다. 예제 입력에서 dobarz adatak 이 주어지는 경우 "da", "od", "ba", "at", "ra", "zk"와 같이 6개의 문자열들이 만들어지게 됩니다. 만약 가장 위의 행을 지워도 테이블의 열을 읽어서 문자열이 중복되지 않는다면, 가장 위의 행을

www.acmicpc.net

테이블의 행을 i-1번째까지 지운 상태에서 i번째행부터의 문자열을 검사한다고 했을 때, 동일한 문자열이 있다면 그 이후 문자열들도 중복이다. 예를 들어, abc와 abc가 중복이면 이후인 bc와 bc, c와 c가 중복이기 때문이다.

반대로 중복이 아니라면 i-1번째 행까지 지웠을 때에도 중복이 아니다. 그 이전에 중복이면 위와 같은 이유로 현재도 중복일 것이기 때문이다. 

 

 

이분 탐색으로 mid번째 행부터 시작하는 문자열(mid-1번째까지는 지워진 상태)을 검사하여 동일한 문자열이 발생한다면 mid 이전의 행을 탐색하고, 만약 동일한 문자열이 발생하지 않는다면 더 큰 값을 찾기 위해 mid이후의 행을 지웠을 때의 문자열들을 검사해서 정답을 구할 수 있다. 중복이 발생하지 않은 행 중 최댓값이 count값으로 정답이 된다.

 

 

문자열의 중복을 검사할 때에는 set을 이용하였다.

 
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
#include <iostream>
#include <string>
#include <set>
using namespace std;
 
int r, c;
int ans;
char table[1000][1000];
 
bool check(int start, int end) {
    set<string> s;
 
    for (int j = 0; j < c; j++) {
        string tmp;
        //start행부터 마지막행까지 읽어서 문자열을 만든다.
        for (int i = start; i <= end; i++) tmp += table[i][j];
 
        //set에 문자열이 존재한다면 바로 false를 리턴한다.
        if (s.find(tmp) == s.end())
            s.insert(tmp);
        else
            return false;
    }
 
    return true;
}
 
void binarySearch(int left, int right, int end) {
    if (left > right) return;
 
    int mid = (left + right) / 2;
 
    //mid번째 행부터 마지막 행까지의 문자열을 검사
    bool flag = check(mid, end);
    
    if (flag) {
        //동일한 문자열이 발생하지 않았다면 mid이후 부분을 검사
        
        //중복이 발생하지 않은 행 중 최댓값이 정답
        if (ans < mid) ans = mid;
        binarySearch(mid + 1, right, end);
    }
    else {
        //동일한 문자열이 발생했다면 mid 이전 부분을 검사
        binarySearch(left, mid - 1end);
    }
 
}
 
int main() {
    ios_base::sync_with_stdio(false);
    cin.tie(0);
 
    cin >> r >> c;
    for (int i = 0; i < r; i++) {
        for (int j = 0; j < c; j++) {
            cin >> table[i][j];
        }
    }
 
    ans = 0;
    
    //처음에 주어지는 테이블에는 열을 읽어서 문자열을 만들 때, 동일한 문자열이 존재하지 않음이 보장되므로 1번째 행부터 검사
    binarySearch(1, r - 1, r - 1);
    cout << ans << '\n';
 
    return 0;
}
Colored by Color Scripter
 

 

'BOJ' 카테고리의 다른 글

[BOJ] 2174. 로봇 시뮬레이션  (0) 2020.04.28
[BOJ] 10711. 모래성  (0) 2020.04.27
[BOJ] 5427. 불  (0) 2020.04.19
[BOJ] 18809. Gaaaaaaaaaarden  (0) 2020.03.23
[BOJ] 18808. 스티커 붙이기  (0) 2020.03.23

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

 

5427번: 불

문제 상근이는 빈 공간과 벽으로 이루어진 건물에 갇혀있다. 건물의 일부에는 불이 났고, 상근이는 출구를 향해 뛰고 있다. 매 초마다, 불은 동서남북 방향으로 인접한 빈 공간으로 퍼져나간다. 벽에는 불이 붙지 않는다. 상근이는 동서남북 인접한 칸으로 이동할 수 있으며, 1초가 걸린다. 상근이는 벽을 통과할 수 없고, 불이 옮겨진 칸 또는 이제 불이 붙으려는 칸으로 이동할 수 없다. 상근이가 있는 칸에 불이 옮겨옴과 동시에 다른 칸으로 이동할 수 있다. 빌딩

www.acmicpc.net

4179번 불! (문제 링크) 문제와 스토리가 비슷한 문제인 것 같다.  (4179 불! 풀이 링크)

이 문제는 테스트 케이스가 최대 100개로 여러 개 있기 때문에 매번의 테스트 케이스에서 배열과, 큐 등 초기화를 잘해주어야 한다.

 

 

 

상근이가 최단 시간으로 탈출하는 경우와 불이 퍼지는 것을 구현하기 위해 BFS를 사용한다.

BFS는 상근이가 이동할 수 있는 곳이 더 없다면 끝나기 때문에 상근이의 좌표를 담은 큐가 비었다면 종료한다.

또는, 탈출에 성공한 경우에 바로 탐색을 종료한다.

 

 

 

큐 2개를 사용하여 상근이와 불의 위치를 각각의 큐에 넣어주고 각각 BFS를 진행한다. 같은 시간 동안 불을 먼저 이동시키고 상근이를 이동시키면 불이 확산되지 않은 곳으로 이동할 수 있다. 매 초마다 불을 먼저 확산시키고 다음에 상근이를 이동시키기 위해서 각자의 큐 사이즈만큼씩만 진행하면 된다. 한 번에 이동할 수 있는 위치만큼(현재 위치로부터 1만큼 이동한 곳)이 큐에 새로 들어가기 때문이다.

 

 

 

상근이가 빌딩 지도 범위를 넘어가면 탈출에 성공한 것이므로 바로 true를 리턴하여 시간을 출력해준다.

BFS가 끝났는데 빌딩을 탈출하지 못했다면 false를 리턴하고 문제 조건에 따라 IMPOSSIBLE을 출력한다.

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 <queue>
#include <cstring>
using namespace std;
 
int h, w, time;
char map[1000][1000];
bool visit[1000][1000];
 
queue<pair<intint> > sq; //상근이 이동 큐
queue<pair<intint> > fq; //불 이동 큐
 
int dx[] = { 0,0,1,-1 };
int dy[] = { 1,-1,0,0 };
 
bool bfs() {
    time = 0;
 
    while (!sq.empty()) {
        int fqsize = fq.size();
        int sqsize = sq.size();
        int x, y, nx, ny;
 
        time++;
 
        //불 이동
        while (fqsize--) {
            x = fq.front().first;
            y = fq.front().second;
            fq.pop();
            for (int k = 0; k < 4; k++) {
                nx = x + dx[k];
                ny = y + dy[k];
                //범위 체크
                if (nx < 0 || ny < 0 || nx >= h || ny >= w) continue;
                //벽
                if (map[nx][ny] == '#'continue;
                //방문 체크
                if (visit[nx][ny]) continue;
 
                fq.push({ nx,ny });
                visit[nx][ny] = true;
            }
        }
 
        //상근이 이동
        while (sqsize--) {
            x = sq.front().first;
            y = sq.front().second;
            sq.pop();
            for (int k = 0; k < 4; k++) {
                nx = x + dx[k];
                ny = y + dy[k];
                if (nx < 0 || ny < 0 || nx >= h || ny >= w) {
                    //탈출 성공
                    return true;
                }
                //벽
                if (map[nx][ny] == '#'continue;
                //방문 체크
                if (visit[nx][ny]) continue;
 
                sq.push({ nx,ny });
                visit[nx][ny] = true;
            }
        }
    }
 
 
//탈출 실패
    return false;
}
 
 
int main() {
    ios_base::sync_with_stdio(false);
    cin.tie(0);
 
    int T;
    cin >> T;
 
    while (T--) {        
        //다음 테스트 케이스를 위해 초기화
        memset(visit, falsesizeof(visit));
        while (!fq.empty()) fq.pop();
        while (!sq.empty()) sq.pop();
        bool exitFlag = false;
 
 
        cin >> w >> h;
        for (int i = 0; i < h; i++) {
            for (int j = 0; j < w; j++) {
                cin >> map[i][j];
                if (map[i][j] == '@') {
                    sq.push({ i,j });
                    map[i][j] = '.';
                    visit[i][j] = true;
                }
                else if (map[i][j] == '*') {
                    fq.push({ i,j });
                    visit[i][j] = true;
                }
            }
        }
 
 
        bool result = bfs();
 
        if (result) cout << time << '\n';
        else cout << "IMPOSSIBLE" << "\n";
    }
 
    return 0;
}
Colored by Color Scripter
 

'BOJ' 카테고리의 다른 글

[BOJ] 10711. 모래성  (0) 2020.04.27
[BOJ] 2866. 문자열 잘라내기  (0) 2020.04.21
[BOJ] 18809. Gaaaaaaaaaarden  (0) 2020.03.23
[BOJ] 18808. 스티커 붙이기  (0) 2020.03.23
[BOJ] 17822. 원판 돌리기  (0) 2020.03.16

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

 

18809번: Gaaaaaaaaaarden

첫째 줄에 정원의 행의 개수와 열의 개수를 나타내는 N(2 ≤ N ≤ 50)과 M(2 ≤ M ≤ 50), 그리고 초록색 배양액의 개수 G(1 ≤ G ≤ 5)와 빨간색 배양액의 개수 R(1 ≤ R ≤ 5)이 한 칸의 빈칸을 사이에 두고 주어진다. 그 다음 N개의 줄에는 각 줄마다 정원의 각 행을 나타내는 M개의 정수가 한 개의 빈 칸을 사이에 두고 주어진다. 각 칸에 들어가는 값은 0, 1, 2이다. 0은 호수, 1은 배양액을 뿌릴 수 없는 땅, 2는 배양

www.acmicpc.net

백준에서 BaaaaaaaaaaarkingDog님이 코딩 테스트 대비 모의고사로 출제하신 완전 탐색+bfs문제이다.

대회가 열렸을 때 들어가서 풀어봤는데 재밌었다. 처음 제출했을때 시간초과가 나서 당황했는데 쓸데없는 작업을 줄여줬더니 통과했다

(꽃이 피는 곳을 저장해놓은 배열을 나중에 처음부터 끝까지 확인해서 꽃의 수를 세줬었는데, 꽃이 필때 꽃을 바로 세는 걸로 바꿔줬다)

 

 

다른 한 문제는 18808 스티커 붙이기(문제 링크)이다. (18808 스티커 붙이기 풀이 링크)

풀이 코드는  BaaaaaaaaaaarkingDog님의 블로그에서도 볼 수 있다.

 

 

 

나는 다음과 같은 방법으로 풀었다.

  1. 정원의 정보를 입력받을 때, 값이 2 (배양액을 뿌릴 수 있는 땅)이면 벡터에 넣어놓는다.
  2. 앞에서 저장해논 배양액을 뿌릴 수 있는 땅들에 대해 배양액을 뿌리지 않는 경우 / 초록색 배양액을 뿌리는 경우 / 빨간색 배양액을 뿌리는 경우를 모두 골라준다. 배양액을 뿌리는 경우에는 뿌리는 배양액의 수 - 1을 매개변수로 넘기고 뿌리지 않는 경우는 배양액의 수를 그대로 넘긴다.
    • 이때 백트랙킹으로 시간을 줄여줘야 하므로
    • 남은 배양액의 수가 남아있는 배양액을 뿌릴 수 있는 땅보다 많으면 안 되므로 더 이상 탐색을 진행하지 않는다.(배양액을 남김없이 사용해야 하고, 땅에 배양액 2개를 사용할 수 없으므로)
    • 초록색과 빨간색 배양액을 둘 다 모두 사용했다면 남은 땅의 경우의 수를 더 정해주지 않고 바로 bfs로 배양액을 퍼뜨려준다.
  3. bfs를 진행할 때에는 초록색 배양액과 빨간색 배양액을 각각 큐에 넣어서 따로 bfs를 진행해주는데, 도착한 시간을 저장하는 배열도 각각 사용한다. 또한, 각각의 큐 사이즈만큼씩 진행해서 bfs를 단계별로 진행한다.(각 초마다 bfs를 진행 가능)
    • 나는 먼저 초록색 배양액을 퍼뜨려줬다. 꽃이 피었거나 / 호수이거나 / 이미 배양액이 뿌려져 있는 경우를 제외하고 바로 퍼뜨리면 된다.
    • 그다음에 큐 사이즈만큼 진행함으로써 초록색 배양액을 뿌린 같은 시간 동안 빨간색 배양액을 뿌릴 수 있다. 빨간색 배양액을 뿌릴 때에는 초록색 배양액이 이미 뿌려져 있어도 같은 시간에 뿌려진 것이라면 꽃이 피므로 이때 꽃의 수를 늘려준다. 이때, 큐에는 넣지 않고 시간만 저장해줘서 다음에 배양액이 퍼지지 않도록 한다.
  4. 배양액을 모두 퍼뜨렸다면 꽃의 수를 저장해놓은 최댓값과 비교한다.

 

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
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
#include <iostream>
#include <cstring>
#include <vector>
#include <queue>
using namespace std;
 
int ans;
int n, m, g, r;
int brownSoilSize; //배양액을 뿌릴 수 있는 땅의 수
int map[50][50];
int gtime[50][50]; //초록색 배양액이 퍼진 시간을 저장
int rtime[50][50]; //빨간색 배양액이 퍼진 시간을 저장
bool flower[50][50]; //꽃이 피었는지 체크
 
int dx[] = { 0,0,1,-1 };
int dy[] = { 1,-1,0,0 };
 
vector<pair<intint> > brownSoil; //배약액을 뿌릴 수 있는 땅의 좌표를 저장
 
vector<pair<intint> > green; //초록색 배양액을 뿌릴 좌표를 저장
vector<pair<intint> > red; //빨간색 배양액을 뿌릴 좌표를 저장
 
 
void bfs() {
    queue<pair<intint> > gq;
    queue<pair<intint> > rq;
 
    //시간을 -1로 초기화
    memset(gtime, -1sizeof(gtime));
    memset(rtime, -1sizeof(rtime));
 
    memset(flower, falsesizeof(flower));
 
 
    //초록색 배양액을 뿌린 땅과 빨간색 배양액을 뿌린 땅을 각각 큐에 넣어준다.
    for (pair<intint> p : green) {
        gq.push(make_pair(p.first, p.second));
        gtime[p.first][p.second] = 0;
    }
    for (pair<intint> p : red) {
        rq.push(make_pair(p.first, p.second));
        rtime[p.first][p.second] = 0;
    }
 
    int fcnt = 0;
    int gqsize;
    int rqsize;
    int x, y, nx, ny;
    while (!gq.empty() || !rq.empty()) {
        gqsize = gq.size();
        rqsize = rq.size();
 
        //초록색 배양액 먼저 bfs
        while (gqsize--) {
            x = gq.front().first;
            y = gq.front().second;
            gq.pop();
 
            //이미 꽃이 피어난 땅은 배양액이 퍼뜨려지지 않는다
            if (flower[x][y]) continue;
 
            for (int k = 0; k < 4; k++) {
                nx = x + dx[k];
                ny = y + dy[k];
 
                //범위 체크
                if (nx < 0 || ny < 0 || nx >= n || ny >= m) continue;
                //방문 체크
                if (gtime[nx][ny] != -1continue;
                //호수인지 체크
                if (map[nx][ny] == 0continue;
 
 
                //빨간색 배양액이 이미 있는 곳인지 체크
                if (rtime[nx][ny] != -1continue;
 
                //위의 조건을 모두 통과했다면 배양액이 퍼진다
                gtime[nx][ny] = gtime[x][y] + 1;
                gq.push(make_pair(nx, ny));
            }
 
        }
 
        
        //빨간색 배양액 bfs
        while (rqsize--) {
            x = rq.front().first;
            y = rq.front().second;
            rq.pop();
 
            for (int k = 0; k < 4; k++) {
                nx = x + dx[k];
                ny = y + dy[k];
 
                //범위체크
                if (nx < 0 || ny < 0 || nx >= n || ny >= m) continue;
                //방문체크
                if (rtime[nx][ny] != -1continue;
                //호수인지 체크
                if (map[nx][ny] == 0continue;                
 
                if (gtime[nx][ny] == -1) {
                    //초록색 배양액이 있는지 체크
                    rtime[nx][ny] = rtime[x][y] + 1;
                    rq.push(make_pair(nx, ny));
                }
                else if (gtime[nx][ny] == rtime[x][y] + 1) {
                    //초록색 배양액이 있지만 동시에 도착한다면
 
                    //꽃이 핀다.
                    flower[nx][ny] = true;
                    rtime[nx][ny] = rtime[x][y] + 1;
 
                    //꽃의 개수 증가
                    fcnt++;
                }
        
            }
 
        }
 
 
    }
 
    //피울 수 있는 꽃의 최대개수를 저장
    if (ans < fcnt) ans = fcnt;
}
 
 
void select(int index,  int gcnt, int rcnt) {
    //남은 배양액의 수가 남아있는 배양액을 뿌릴 수 있는 땅보다 많으면 안된다(배양액을 남김없이 사용해야 하므로)
    if (gcnt + rcnt > (brownSoilSize - index)) return;
    if (gcnt == 0 && rcnt == 0) {
        //배양액을 모두 골랐다면 bfs로 배양액을 뿌려준다.
        bfs();
        return;
    }
    if (index == brownSoilSize) return;
 
    int x = brownSoil[index].first;
    int y = brownSoil[index].second;
 
    //index번째 땅에 배양액을 뿌리지 않는 경우
    select(index + 1, gcnt, rcnt);
 
    //index번째 땅에 초록색 배양액을 뿌리는 경우
    if (gcnt > 0) {
        green.push_back(make_pair(x, y));
        select(index + 1, gcnt - 1, rcnt);
        green.pop_back();
    }
        
    //index번째 땅에 빨간색 배양액을 뿌리는 경우
    if (rcnt > 0) {
        red.push_back(make_pair(x, y));
        select(index + 1, gcnt, rcnt - 1);
        red.pop_back();
    }
 
}
 
int main() {
    ios_base::sync_with_stdio(false);
    cin.tie(0);
 
    ans = 0;
    cin >> n >> m >> g >> r;
 
    for (int i = 0; i < n; i++) {
        for (int j = 0; j < m; j++) {
            cin >> map[i][j];
            //배양액을 뿌릴 수 있는 땅이면 벡터에 넣어놓는다.
            if (map[i][j] == 2) brownSoil.push_back(make_pair(i, j));
        }
    }
 
 
    brownSoilSize = brownSoil.size();
    select(0, g, r);
 
    
    cout << ans << '\n';
 
 
    return 0;
}
Colored by Color Scripter
 

'BOJ' 카테고리의 다른 글

[BOJ] 2866. 문자열 잘라내기  (0) 2020.04.21
[BOJ] 5427. 불  (0) 2020.04.19
[BOJ] 18808. 스티커 붙이기  (0) 2020.03.23
[BOJ] 17822. 원판 돌리기  (0) 2020.03.16
[BOJ] 10757. 큰 수 A + B  (0) 2020.03.10

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

 

18808번: 스티커 붙이기

혜윤이는 최근에 다양한 대회를 참여하면서 노트북에 붙일 수 있는 스티커들을 많이 받았다. 스티커는 아래와 같이 사각 모눈종이 위에 인쇄되어 있으며, 스티커의 각 칸은 상하좌우로 모두 연결되어 있다. 또한 모눈종이의 크기는 스티커의 크기에 꼭 맞아서, 상하좌우에 스티커가 포함되지 않는 불필요한 행이나 열이 존재하지 않는다. 아래는 올바른 모눈종이의 예시이다. 주황색 칸은 스티커가 붙은 칸을, 하얀색 칸은 스티커가 붙지 않은 칸을 나타낸다. 반면 아래는 올바

www.acmicpc.net

백준에서 BaaaaaaaaaaarkingDog님이 코딩 테스트 대비 모의고사로 출제하신 시뮬레이션 문제이다. 대회가 열렸을 때 들어가서 풀어봤는데 재밌었다. 총 두 문제였는데 나머지 한 문제는 18809번 Gaaaaaaaaaarden (문제 링크)이다. (풀이 링크)

스티커 붙이기 문제가 좀 더 쉽다. 

 

 

 

먼저 매번

 sticker를 저장할 int배열 / 모니터에 스티커가 붙여졌는지 확인할 bool 배열 / 스티커를 90도 회전시킬 때 임시로 사용할 int 배열

이렇게 세 가지 배열을 사용하였다.

 

 

문제에서 나와있는 스티커를 붙이는 조건은 다음과 같다.

  1. 스티커를 회전시키지 않고 모눈종이에서 떼어낸다.
  2. 다른 스티커와 겹치거나 노트북을 벗어나지 않으면서 스티커를 붙일 수 있는 위치를 찾는다. 혜윤이는 노트북의 위쪽부터 스티커를 채워 나가려고 해서, 스티커를 붙일 수 있는 위치가 여러 곳 있다면 가장 위쪽의 위치를 선택한다. 가장 위쪽에 해당하는 위치도 여러 곳이 있다면 그중에서 가장 왼쪽의 위치를 선택한다.
  3. 선택한 위치에 스티커를 붙인다. 만약 스티커를 붙일 수 있는 위치가 전혀 없어서 스티커를 붙이지 못했다면, 스티커를 시계 방향으로 90도 회전한 뒤 2번 과정을 반복한다.
  4. 위의 과정을 네 번 반복해서 스티커를 0도, 90도, 180도, 270도 회전시켜 봤음에도 스티커를 붙이지 못했다면 해당 스티커를 붙이지 않고 버린다.

 

 

위의 조건에 따라 먼저 입력받은 스티커를 노트북의 위쪽부터 붙일 수 있는지 확인한다. 

(0, 0)부터 (N-R, M-C) 칸까지 각 칸을 스티커의 시작점으로 했을 때의 스티커를 붙이는 경우를 순차적으로 해보고

붙일 수 있다면 바로 그 자리에 붙인다. 문제 조건에서 스티커를 붙일 우선순위는 가장 먼저 가장 위쪽, 그다음에 가장 왼쪽이므로 일반적인 for문으로 돌리고 찾는 순간 바로 탐색을 종료하면 자연스럽게 구현할 수 있다.

 

 

 

스티커를 붙일 수 있는지는 단순히 스티커를 저장해놓은 배열모니터에 스티커가 붙어있는지 확인할 배열 비교해주면 된다.

비교할 때나 모니터에 스티커를 붙여줄 때, 확인할 모니터의 시작점을 주의해야 한다.

스티커는 매번 (0, 0)부터 검사해주면 되지만 모니터는 스티커를 붙이기 시작하는 칸부터 검사해야 하므로

스티커를 모니터의 (x, y) 칸부터 붙여본다고 한다면 스티커의 (i, j)를 확인할 때는 모니터의(i+x, j+y) 칸을 비교해주어야 한다.

 

 

 

모니터에 이미 스티커가 있는데 모눈종이 위에도 스티커가 있다면 붙일 수 없으므로 바로 false를 리턴해서 다음 경우로 넘어간다.

붙일 수 있다면 모니터에 스티커를 붙여주고 다음 스티커로 넘어간다.

 

 

 

모든 칸에서 시작해도 스티커를 붙일 수 없다면 90도 회전해서 똑같은 과정을 반복한다.

90도씩 회전시켜서 모든 경우(0도, 90도, 180도, 270도)를 해봤는데도 붙일 수 없다면 문제 조건에 따라 해당 스티커는 버린다.

 

 

90도 회전을 시킬 때는 위의 그림과 같이 tmp배열의 index를 기준으로 복사해줬다.

90도 회전하게 되면 행의 수(R)와 열의 수(C)가 되므로 for문에서 i는 C 전까지, j는 R 전까지 해주면 된다.

for문을 이용해서 tmp배열은 순차적으로 채워주면 되고, 스티커 배열은 i추가적인 변수 l 을 사용해서 위의 그림에 나와있는 순서대로 값이 들어가도록 구현했다. 코드로 보면 다음과 같다.

 

for (int i = 0; i < c; i++) {

        for (int j = 0, l = r-1; j < r; j++, l--) {

            tmparr[i][j] = sticker[l][i];

        }

    }

 

tmp배열에 모두 복사했다면 R과 C의 값을 아예 바꿔주고 tmp배열의 값을 원래 스티커 배열로 다시 옮겨준다.

 

 

 

k번의 스티커를 모두 붙여 본 후에 모니터에 스티커가 붙여져 있는지 확인하는 배열을 검사해서 최종 답을 구한다.

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
#include <iostream>
#include <cstring>
using namespace std;
 
int n, m, k;
int r, c;
 
bool map[40][40];
int sticker[10][10];
int tmparr[10][10];
 
 
void rotate() {
    memset(tmparr, 0sizeof(tmparr));
 
    //회전시킨 모양을 tmparr에 임시 저장
    for (int i = 0; i < c; i++) {
        for (int j = 0, l = r-1; j < r; j++, l--) {
            tmparr[i][j] = sticker[l][i];
        }
    }
 
    //90도 회전하므로 행과 열을 바꿔준다
    int tmp = r;
    r = c;
    c = tmp;
 
    //다시 스티커 배열에 tmparr배열을 복사
    memset(sticker, 0sizeof(sticker));
    for (int i = 0; i < r; i++) {
        for (int j = 0; j < c; j++) {
            sticker[i][j] = tmparr[i][j];
        }
    }
}
 
 
void putSticker(int x, int y) {
    for (int i = 0; i < r; i++) {
        for (int j = 0; j < c; j++) {
            //모눈 종이에서 스티커인 부분(1)을 노트북에 붙인다
            if (sticker[i][j] == 1) map[i + x][j + y] = true;
        }
    }
}
 
 
bool check(int x, int y) {
    for (int i = 0; i < r; i++) {
        for (int j = 0; j < c; j++) {
            //스티커를 붙일 곳에 이미 스티커가 붙어있다면 바로 false를 리턴
            if (map[i + x][j + y] && sticker[i][j] == 1return false;
        }
    }
    return true;
}
 
 
bool solve() {
    for (int i = 0; i <= n - r; i++) {
        for (int j = 0; j <= m - c; j++) {
            //i,j를 시작점으로 스티커를 붙여본다.
            if (check(i, j)) {
                //스티커를 붙일 수 있다면 붙이고 바로 true를 리턴
                putSticker(i, j);
                return true;
            }
        }
    }
    return false;
}
 
 
int main() {
    ios_base::sync_with_stdio(false);
    cin.tie(0);
 
    cin >> n >> m >> k;
 
    while (k--) {
        cin >> r >> c;
 
        for (int i = 0; i < r; i++) {
            for (int j = 0; j < c; j++) {
                cin >> sticker[i][j];
            }
        }
 
 
        bool flag = false;
        for (int i = 0; i < 4; i++) {
            flag = solve();
 
            //붙일 수 있는 곳이 있다면 break
            if (flag) break;
            else rotate(); //붙일 수 있는 곳이 없다면 90도 회전
        }
 
 
    }
 
 
    int ans = 0;
    //스티커가 붙어 있는 칸을 센다.
    for (int i = 0; i < n; i++) {
        for (int j = 0; j < m; j++) {
            if (map[i][j]) ans++;
        }
    }
 
 
    cout << ans << '\n';
    
 
    return 0;
}
Colored by Color Scripter
 

'BOJ' 카테고리의 다른 글

[BOJ] 5427. 불  (0) 2020.04.19
[BOJ] 18809. Gaaaaaaaaaarden  (0) 2020.03.23
[BOJ] 17822. 원판 돌리기  (0) 2020.03.16
[BOJ] 10757. 큰 수 A + B  (0) 2020.03.10
[BOJ] 1331. 나이트 투어  (0) 2020.03.03

+ Recent posts