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

 

17822번: 원판 돌리기

반지름이 1, 2, ..., N인 원판이 크기가 작아지는 순으로 바닥에 놓여있고, 원판의 중심은 모두 같다. 원판의 반지름이 i이면, 그 원판을 i번째 원판이라고 한다. 각각의 원판에는 M개의 정수가 적혀있고, i번째 원판에 적힌 j번째 수의 위치는 (i, j)로 표현한다. 수의 위치는 다음을 만족한다. (i, 1)은 (i, 2), (i, M)과 인접하다. (i, M)은 (i, M-1), (i, 1)과 인접하다. (i, j)는 (i, j-1), (i, j

www.acmicpc.net

원판을 회전시키며 인접한 수를 비교하고, 같다면 숫자를 지우는 작업을 반복하는 시뮬레이션 문제이다.

 

 

 

T회만큼 반복하면서 x의 배수인 원판들을 모두 회전시킨 후 인접한 같은 수를 저장해놓았다가 나중에 한 번에 지워준다. (미리 지워서 0으로 만들어버리면 다른 방향으로 인접한 수를 지울 수 없다)

인접한 수 중 같은 숫자인게 없다면 원판에 있는 숫자의 평균을 구해서 평균보다 작으면 1을 증가시켜주고 크면 1을 감소시킨다. 여기서 지워진 숫자는 제외하고 평균을 구해야 한다

 

 

 

//k칸만큼 회전시키기

먼저 k칸만큼 시계방향 회전시킨 방법은 아래와 같이 했다. 예를 들어 k가 4인 경우이다.

편의상 배열은 인덱스가 1부터 시작하도록 구현했다.

 

 

원래 원판에 있던 숫자를 첫 번째부터 tmp배열의 k만큼 떨어진 곳인 5번 자리부터 복사한다. 인덱스가 배열 밖으로 넘어가면 안 되므로 작업을 tmp배열의 마지막 부분인 6번 인덱스까지만 먼저 진행한다.

 

 

 

 

그다음 아래 그림처럼 원판의 나머지 부분을 이어서 tmp배열의 처음 칸부터 k번 인덱스까지 순차적으로 채워주면 된다.

 

시계 반대 방향으로 1번 회전시킨 것은 시계 방향으로 M-1번 회전시킨 것과 같고

마찬가지로 시계 반대 방향으로 2번 회전시킨 것은 시계 방향으로 M-2번 회전시킨 것과 같으므로

같은 함수를 이용해서 회전 횟수를M-k번으로 해주면 된다.

 

 

 

//인접한 숫자 중 같은 수 찾기

 

문제에 나와있는 인접한 수의 조건은 다음과 같다.

  • (i, 1)은 (i, 2), (i, M)과 인접하다.
  • (i, M)은 (i, M-1), (i, 1)과 인접하다.
  • (i, j)는 (i, j-1), (i, j+1)과 인접하다. (2 ≤ j ≤ M-1)
  • (1, j)는 (2, j)와 인접하다.
  • (N, j)는 (N-1, j)와 인접하다.
  • (i, j)는 (i-1, j), (i+1, j)와 인접하다. (2 ≤ i ≤ N-1)

그냥 같은 원판(같은 행) 안에서 바로 양 옆이 인접한 수이고, 바로 위나 아래에 있는 원판 중에서는 같은 열이 인접한 수이다. 그래서 같은 원판 안에서는 마지막 숫자의 옆이 다시 첫 번째 숫자인 것을 처리해주어야 하지만

서로 다른 원판들의 위아래를 비교할 때는 맨 위의 원판과 맨 아래의 원판은 인접한 것이 아니므로 따로 처리해줄 필요는 없다.

 

 

 

//인접한 같은 수 지우기

또, 인접한 같은 수를 미리 지워서 0으로 만들어버리면 다른 방향으로 인접한 수와 비교할 수 없으므로 나중에 한 번에 지워줘야 한다. 그러기 위해서 인접한 같은 수를 가진 좌표들을 저장해놓아야 한다.

중복되는 좌표도 발생할 것 같아서 set을 이용하였다. 인접한 수를 찾으면 set에 원판의 위치(i, j)를 pair로 넣어주고, 나중에 set에서 한 번에 위치를 꺼내서 원판의 해당 위치의 숫자를 지워준다

(이때 나는 한쪽 방향으로만 비교하도록 구현했기 때문에 인접한 같은 두 수의 위치를 모두 넣어줬다)

 

 

 

//평균 구해서 더하고 빼주기

위에서 인접한 같은 수가 없어서 지워진 수가 하나도 없다면 문제 조건에 따라

원판에 있는 수들의 평균을 구해서(지워진 수는 제외) 평균보다 높다면 1을 빼주고 평균보다 낮다면 1을 더해준다.

c++에서 int형으로 몫을 구했다면 소수점을 제외한 몫만 저장하므로 나누어 떨어지지 않는 경우를 따로 처리해준다.

 

나누어 떨어지는 경우는 정말 값이 같은 경우이므로 값을 바꿔줄 필요가 없고

나머지가 있다면 실제 값은 몫+나머지 이므로 원판의 값이 평균보다 더 작은 것이므로 1을 더해준다.

 

 

 

//최종 답

T번의 회전이 모두 끝나면 원판의 숫자를 모두 더해준 값이 정답이다.

 

 

 

 

개인적으로 실수한 부분들 메모...

  1. 평균을 구하는 과정에서 0으로 나누는 경우가 발생하면 안 된다.
  2. 문제 조건에서 평균보다 큰 경우와 작은 경우에 대해만 나와있으므로 같은 경우에는 값이 변하지 않는다.
  3. n과 m을 또 반대로 씀... 문제에서 반대로 해놓은 것이 아니었음에도 불구하고...
  4. 문제 조건을 제대로 안 읽고, 인접한 수가 없는 경우 평균을 구해서 값을 변경시켜주는 부분을 안봄...
  5. 인접한 같은 수 두 개를 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
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
#include <iostream>
#include <set>
using namespace std;
 
int n, m, t;
int board[51][51];
int tmp[51][51];
 
 
void rotate(int i, int count) {
    //i번 원판을 회전
 
    int index = 1;
    for (int j = count+1; j <= m; j++) {
        tmp[i][j] = board[i][index++];
    }
 
    for (int j = 1; j <= count; j++) {
        tmp[i][j] = board[i][index++];
    }
 
 
    //board로 다시 복사
    for (int j = 1; j <= m; j++) {
        board[i][j] = tmp[i][j];
    }
}
 
 
bool removeAdj() {
 
    set<pair<intint> > s;
 
    for (int i = 1; i <= n; i++) {
        for (int j = 1; j <= m; j++) {
 
            if (board[i][j] == 0continue;
 
 
            //같은 원판 비교
            if (j == m) {
                //마지막열의 다음은 첫번째열
                if (board[i][m] == board[i][1]) {
                    s.insert(make_pair(i, m));
                    s.insert(make_pair(i, 1));
                }
            }
            else {
                if (board[i][j] == board[i][j + 1]) {
                    s.insert(make_pair(i, j));
                    s.insert(make_pair(i, j + 1));
                }
            }
            
        }
    }
 
 
    //위 아래 원판 비교
    for (int j = 1; j <= m; j++) {
        for (int i = 1; i < n; i++) {
            //지워진 애는 넘어간다.
            if (board[i][j] == 0continue;
 
            //아래있는 원판과 비교
            if (board[i][j] == board[i+1][j]) {
                s.insert(make_pair(i, j));
                s.insert(make_pair(i + 1, j));
            }
        
        }
    }
 
 
    //인접한 수 중 같은게 없으면 바로 false를 리턴
    if (s.empty()) return false;
 
 
    //set에 들어있는 좌표를 사용하여 모두 지우고 true를 리턴
    set<pair<intint>> ::iterator iter;
    for (iter = s.begin(); iter != s.end(); iter++) {
        board[iter->first][iter->second] = 0;
    }
 
    return true;
}
 
void calc() {
 
    int sum = 0;
    int total = 0;
    for (int i = 1; i <= n; i++) {
        for (int j = 1; j <= m; j++) {
            if (board[i][j] == 0continue;
 
            total += 1;
            sum += board[i][j];
        }
    }
 
    //숫자가 모두 지워져있다면 바로 리턴
    if (total == 0return;
    int avg = sum/total;
 
 
    for (int i = 1; i <= n; i++) {
        for (int j = 1; j <= m; j++) {
            if (board[i][j] == 0continue;
 
            if (board[i][j] > avg) board[i][j] -= 1;
            else if(board[i][j] < avg) board[i][j] += 1;
            else {
                //나머지가 있다면 board[i][j]의 값이 더 작으므로 +1
                if (sum%total != 0) board[i][j] += 1;
            }
        }
    }
 
}
 
int main() {
    ios_base::sync_with_stdio(false);
    cin.tie(0);
 
    cin >> n >> m >> t;
 
    for (int i = 1; i <= n; i++) {
        for (int j = 1; j <= m; j++) {
            cin >> board[i][j];
        }
    }
 
    int x, d, k;
    while (t--) {
        cin >> x >> d >> k;
 
 
        for (int i = 1; i <= n; i++) {
            //x의 배수인 원판을 회전
            if (i % x != 0continue;
 
            
            if (d == 0) {
                //시계방향
                rotate(i, k);
            }
            else {
                //반시계방향
                rotate(i, m - k);
            }
 
        }
        
 
        bool isRemoved = removeAdj();
        if (!isRemoved) {
            //인접한 수 중 같은게 없다면 평균과 비교해 1을 더하거나 빼준다.
            calc();
        }
    }
 
 
    //t회 후에 숫자의 총 합
    int ans = 0;
    for (int i = 1; i <= n; i++) {
        for (int j = 1; j <= m; j++) {
            ans += board[i][j];
        }
    }
 
 
    cout << ans << '\n';
 
 
    return 0;
}
Colored by Color Scripter
 

'BOJ' 카테고리의 다른 글

[BOJ] 18809. Gaaaaaaaaaarden  (0) 2020.03.23
[BOJ] 18808. 스티커 붙이기  (0) 2020.03.23
[BOJ] 10757. 큰 수 A + B  (0) 2020.03.10
[BOJ] 1331. 나이트 투어  (0) 2020.03.03
[BOJ] 4179. 불!  (0) 2020.02.17

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

 

10757번: 큰 수 A+B

두 정수 A와 B를 입력받은 다음, A+B를 출력하는 프로그램을 작성하시오.

www.acmicpc.net

수의 범위가 long long의 범위도 넘어가는 경우에는 문자열로 구현해주어야 한다.

이 문제를 풀어놓으면 활용해서 풀 수 있는 문제들이 꽤 있다. (1914 하노이탑, 2407 조합, 10826 피보나치 수 4 등)

15353번 큰 수 A + B (2) 문제는 아예 똑같은 코드로 풀 수 있다.

 

 

먼저 문자열 두 개를 입력받았으면 덧셈을 해주기 위해서 자릿수를 맞춰주어야 한다.

길이를 비교하고 길이가 더 짧은 곳에 0을 붙여준다.

예를 들어 123456과 123456789이라면 123456 앞에 두 수의 길이의 차이인 3만큼 0을 붙여줘서 000123456을 만들어준 후에 덧셈을 진행한다.

 

 

각각의 자리에서 더해주고 9보다 큰 값이 있다면 다음 자릿수의 덧셈에서 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
#include <iostream>
#include <algorithm>
#include <string>
using namespace std;
 
int main() {
    ios_base::sync_with_stdio(false);
    cin.tie(0);
 
    string a, b;
    string ans = "";
    cin >> a >> b;
 
    int n = a.length() - 1;
    int m = b.length() - 1;
 
 
    //자릿수 맞춰준다.
    string tmp = "";
    if (n < m) {
        for (int i = 0; i < m - n; i++) {
            tmp += "0";
        }
        a = tmp + a;
    }
    else if (n > m) {
        for (int i = 0; i < n - m; i++) {
            tmp += "0";
        }
        b = tmp + b;
    }
 
    
    int len = a.size(); //자릿수 위에서 맞춰줬으므로 길이 아무거나 상관없음
    int x, y, z;
    int up = 0;
 
//1의 자리부터 진행
    for (int i = len - 1; i >= 0; i--) {
        x = a[i] - '0';
        y = b[i] - '0';
        z = x + y;
        if (up == 1) {
            z += 1;
            up = 0;
        }
 
 
        if (z > 9) {
            ans += to_string(z % 10);
            up = 1;
        }
        else {
            ans += to_string(z);
        }
    }
    
    if (up == 1) ans += "1";
 
 
    reverse(ans.begin(), ans.end());
 
    cout << ans << '\n';
 
 
    return 0;
}
Colored by Color Scripter
 

'BOJ' 카테고리의 다른 글

[BOJ] 18808. 스티커 붙이기  (0) 2020.03.23
[BOJ] 17822. 원판 돌리기  (0) 2020.03.16
[BOJ] 1331. 나이트 투어  (0) 2020.03.03
[BOJ] 4179. 불!  (0) 2020.02.17
[BOJ] 1938. 통나무 옮기기  (0) 2020.02.12

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

 

1331번: 나이트 투어

나이트 투어는 체스판에서 나이트가 모든 칸을 정확히 한 번씩 방문하며, 마지막으로 방문하는 칸에서 시작점으로 돌아올 수 있는 경로이다. 다음 그림은 나이트 투어의 한 예이다. 영식이는 6×6 체스판 위에서 또 다른 나이트 투어의 경로를 찾으려고 한다. 체스판의 한 칸은 A~F 중의 알파벳 하나와 1~6 중의 숫자 하나로 나타낼 수 있다. 영식이의 나이트 투어 경로가 주어질 때, 이것이 올바른 것이면 Valid, 올바르지 않으면 Invalid를 출력하는 프

www.acmicpc.net

  1. 나이트의 이동 경로가 맞는지
  2. 모든 칸을 정확히 한 번씩만 방문했는지
  3. 마지막으로 방문하는 칸에서 시작점으로 돌아올 수 있는지

모든 이동에서 위의 1번과 2번의 경우를 확인해주고 마지막 칸에서 3번 조건을 확인한다.

하나라도 조건에 맞지 않다면 Invalid를 출력하고 모든 조건을 만족한다면 Valid를 출력한다.

 

 

현재 좌표가 x, y 라고 할 때, 나이트가 이동할 수 있는 경로는 위의 그림과 같다.

즉, (x, y)와 이동할 좌표의 차이의 절댓값이

x가 1인 경우에 y는 2이고, x가 2인 경우에 y는 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
#include <iostream>
#include <string>
#include <cmath>
#define MAX 36
using namespace std;
 
 
bool visit[36][36];
 
bool check(int x, int y, int nx, int ny) {
    int diffx = abs(nx - x);
    int diffy = abs(ny - y);
 
    if (diffx == 1 && diffy == 2return true;
    else if (diffx == 2 && diffy == 1return true;
 
    return false;
}
 
 
int main() {
    ios_base::sync_with_stdio(false);
    cin.tie(0);
 
    bool valid = true;
 
    string now, next;
    
    //시작칸 입력
    cin >> now;
 
    int sx, sy;
    int x, y, nx, ny;
 
    //시작칸 좌표
    sx = now[0- 'A';
    sy = now[1- '1';
    
    int cnt = 1;
    x = sx;
    y = sy;
    visit[x][y] = true;
 
 
    while (cnt++ < MAX) {
        cin >> next;
        if (!valid) continue;
 
 
        nx = next[0- 'A';
        ny = next[1- '1';
 
 
        //방문 확인
        if (visit[nx][ny]) {
            valid = false;
            continue;
        }
 
 
        //방문 표시
        visit[nx][ny] = true;
 
 
        //나이트의 이동경로가 맞는지 검사
        bool flag = check(x, y, nx, ny);
        //나이트의 이동경로로 갈 수 없다면 Invalid
        if (!flag) valid = false;
 
        
        //이동한 좌표를 현재좌표로 바꿔준다.
        now = next;
        x = nx;
        y = ny;
    }
 
 
    //다시 시작칸으로 돌아갈 수 있는지 검사
    if (!check(x,y,sx,sy)) valid = false;
 
 
    if(valid) cout << "Valid" << '\n';
    else cout << "Invalid" << "\n";
 
 
    return 0;
}
Colored by Color Scripter
 

'BOJ' 카테고리의 다른 글

[BOJ] 17822. 원판 돌리기  (0) 2020.03.16
[BOJ] 10757. 큰 수 A + B  (0) 2020.03.10
[BOJ] 4179. 불!  (0) 2020.02.17
[BOJ] 1938. 통나무 옮기기  (0) 2020.02.12
[BOJ] 17780. 새로운 게임  (0) 2020.02.03

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

 

4179번: 불!

문제 지훈이는 미로에서 일을 한다. 지훈이를 미로에서 탈출하도록 도와주자! 미로에서의 지훈이의 위치와 불이 붙은 위치를 감안해서 지훈이가 불에 타기전에 탈출할 수 있는지의 여부, 그리고 얼마나 빨리 탈출할 수 있는지를 결정해야한다. 지훈이와 불은 매 분마다 한칸씩 수평또는 수직으로(비스듬하게 이동하지 않는다)  이동한다.  불은 각 지점에서 네 방향으로 확산된다.  지훈이는 미로의 가장자리에 접한 공간에서 탈출할 수 있다.  지훈이와 불은 벽이 있는 공간

www.acmicpc.net

지훈이가 미로를 탈출할 수 있는 경우에는 가장 빠른 탈출시간을 출력하는 BFS문제이다.

 

 

3055번 탈출 문제(문제 링크)랑 비슷한 것 같다.

탈출 문제에서는 물이 퍼지는 시간을 먼저 BFS로 저장해놓고, 고슴도치가 이동할 곳의 시간이 물이 퍼진시간보다 작은 경우에만 이동할 수 있도록 구현했다. (문제 풀이 링크)

 

 

이번에도 똑같이 풀어봤는데 정답은 뜨지만 메모리를 많이 써서 다른 방법으로 다시 풀어봤다.

먼저 지훈이가 이동할 좌표와 불이 확산될 좌표를 각각 다른 큐에 넣어놓고 이동시킨다.

그리고 매분마다 불을 먼저 이동시키고 지훈이를 이동시키면 불이 확산되지 않은 곳으로 이동할 수 있다.

 

 

(입력받을 때 지훈이의 위치는 지나갈 수 있는 공간이므로 '.'으로 바꿔줘야 한다.)

 

 

미로를 탈출하는 경우는 입력으로 주어지는 지도 밖으로 벗어나는 경우이므로

배열의 크기를 더 크게 잡아서 범위를 벗어나는 경우에 탈출한 것으로 본다.

아래 그림처럼 지도를 (1, 1)부터 입력받으면 지훈이의 위치가 0행이거나 0열이거나 R+1행이거나 C+1 열인 경우 미로를 탈출한다.

 

 

BFS를 진행할 때에는, 먼저 지훈이가 이동할 수 있는 곳이 더 없다면 끝나기 때문에 지훈이의 좌표를 담은 큐가 비었다면 BFS를 종료한다. 그리고 매분마다 불을 먼저 확산시키고 다음에 지훈이를 이동시키기 위해서

각자의 큐사이즈만큼씩만 진행하면 된다.

한 번에 이동할 수 있는 위치만큼(현재 위치로부터 1만큼 이동한 곳)이 큐에 새로 들어가기 때문이다.

 

 

지훈이가 미로를 탈출했다면 그때의 시간을 출력하고 바로 리턴해서 종료시킨다.

이 부분에서 종료되지 않고 BFS탐색을 끝낸다면 탈출이 불가능한 경우이므로 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
#include <iostream>
#include <queue>
using namespace std;
 
int r, c;
char map[1001][1001];
bool visit[1001][1001];
int dx[] = { 0,0,1,-1 };
int dy[] = { 1,-1,0,0 };
 
 
int main() {
    ios_base::sync_with_stdio(false);
    cin.tie(0);
 
    //불이 이동할 큐
    queue<pair<intint> > fq;
    //지훈이 이동할 큐
    queue<pair<intint> > jq;  
    
 
    cin >> r >> c;
    for (int i = 1; i <= r; i++) {
        for (int j = 1; j <= c; j++) {
            cin >> map[i][j];
 
            if (map[i][j] == 'J') {
                jq.push(make_pair(i, j));
                visit[i][j] = true;
 
                map[i][j] = '.';
            }
            else if (map[i][j] == 'F') {
                fq.push(make_pair(i, j));
                visit[i][j] = true;
            }
        }
    }
 
 
    int x, y;
    int time = 0;
    while (!jq.empty()) {
        int jqsize = jq.size();
        int fqsize = fq.size();
        int 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 > r || ny > c) continue;
                //벽이면 못간다
                if (map[nx][ny] == '#'continue;
                //방문 체크
                if (visit[nx][ny]) continue;
 
 
                fq.push(make_pair(nx, ny));
                visit[nx][ny] = true;
            }
        }
 
 
        //지훈이 이동
        while (jqsize--) {
            x = jq.front().first;
            y = jq.front().second;
            jq.pop();
 
            for (int k = 0; k < 4; k++) {
                nx = x + dx[k];
                ny = y + dy[k];
 
                //벽이면 못간다
                if (map[nx][ny] == '#'continue;
                //이미 이동한 곳이거나,  불이 이미 확산되어 있거나 같은 시간에 확산된다
                if (visit[nx][ny]) continue;
                
                //탈출한 경우 바로 종료
                if (nx == 0 || ny == 0 || nx == r + 1 || ny == c + 1) {
                    cout << time << '\n';
                    return 0;
                }
 
                jq.push(make_pair(nx, ny));
                visit[nx][ny] = true;
            }
        }
 
 
    }
 
 
    //위에서 종료되지 못한 경우
    cout << "IMPOSSIBLE" << '\n';
 
    return 0;
}
Colored by Color Scripter
 

'BOJ' 카테고리의 다른 글

[BOJ] 10757. 큰 수 A + B  (0) 2020.03.10
[BOJ] 1331. 나이트 투어  (0) 2020.03.03
[BOJ] 1938. 통나무 옮기기  (0) 2020.02.12
[BOJ] 17780. 새로운 게임  (0) 2020.02.03
[BOJ] 17837. 새로운 게임 2  (0) 2020.02.03

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

 

1938번: 통나무 옮기기

첫째 줄에 주어진 평지의 한 변의 길이 N이 주어진다. (4<=N<=50) 주어진다. 이어서 그 지형의 정보가 0, 1, B, E로 이루어진 문자열로 주어진다. 한 줄에 입력되는 문자열의 길이는 N이며 입력 문자 사이에는 빈칸이 없다. 통나무와 최종 위치의 개수는 1개이다.

www.acmicpc.net

처음 주어진 통나무의 위치로부터 가능한 동작(U, D, R, L, T)을 모두 사용하여 도착지점까지 이동하면 된다.

이동할 때마다 매번 U, D, R, D, T를 해보고 이동이 가능하다면 동작수를 증가시켜서 이동하면 된다.

 

 

 

이동했을 때 범위를 벗어나거나 / 잘려지지 않은 나무가 있는 경우가 아니라면 이동 가능하다.

최소 동작수를 구해야 하므로 이미 갔던 좌표에도 다시 갈 필요가 없다.

하지만 통나무의 방향이 가로방향으로 온 것과 세로 방향으로 온 것은 구분해주어야 한다.

특히, 회전(T)을 할 때에는 3X3 정사각형 위치에 나무가 있거나 범위를 벗어나지 않는지 모두 확인해주어야 한다.

 

 

 

처음 입력받을 때 처음 통나무 위치의 좌표( pair<intint> start[3] )

이동할 위치의 좌표( pair<intint> dest[3] )를 각각 배열에 저장한다.

그리고 실제 이동은 통나무의 길이는 3으로 정해져 있으므로 중심점의 좌표만 이용하여 이동한다.

 

처음 방향은 이어진 두 좌표가 같은 x좌표라면 (같은 행에 있다면) 가로 방향이고

그렇지 않다면(다른 x좌표 == 다른 행) 세로 방향이다.

 중심점의 좌표와 방향을 이용하여 통나무를 이동시킨다.

 

 

 

이동할 통나무의 정보는 Tree라는 구조체를 만들어서 저장하였다.

Tree는 x좌표와 y좌표 dir(방향), cnt(동작수)를 가지고 있다.

방향은 0이면 가로방향 1이면 세로 방향으로 해놓았다.

이동이 가능할 때마다 이동할 중심 좌표의 위치와, 방향, 이전 동작수 +1을 해서 새로 큐에 넣어주면 된다.

 

 

 

최소 동작 수로 이동시키기 위해서 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
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
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
#include <iostream>
#include <queue>
using namespace std;
 
int n;
char map[50][50];
 
pair<intint> start[3];
pair<intint> dest[3];
 
//도착지점의 방향
int destDir;
 
int dx[] = { -1,-1,-1,0,0,1,1,1 };
int dy[] = { -1,0,1,-1,1,-1,0,1 };
 
 
//visit[x][y][0] = 가로방향인 상태에서 x,y 에 방문
//visit[x][y][1] = 세로방향인 상태에서 x,y에 방문
bool visit[50][50][2];
 
 
struct Tree {
    int x, y;
    int dir;
    int cnt;
    Tree(int i, int j, int d, int c) {
        x = i;
        y = j;
        dir = d;
        cnt = c;
    }
};
 
 
 
bool turn(int x, int y) {
 
    int nx, ny;
    //중심점에 인접한 8방향을 검사
    for (int k = 0; k < 8; k++) {
        nx = x + dx[k];
        ny = y + dy[k];
 
        //범위를 벗어나면 회전 불가능
        if (nx < 0 || ny < 0 || nx >= n || ny >= n) return false;
        //나무 있으면 회전 불가능
        if (map[nx][ny] == '1'return false;
    }
 
 
    //위의 조건에서 걸리지 않으면 회전 가능
    return true;
}
 
 
 
int solve(int x, int y, int dir) {
    queue<Tree> q;
 
      //x좌표, y좌표, 방향, 동작수
    q.push(Tree(x, y, dir, 0));
    visit[x][y][dir] = true;
 
 
    int cnt;
    while (!q.empty()) {
        x = q.front().x;
        y = q.front().y;
        dir = q.front().dir;
        cnt = q.front().cnt;
        q.pop();
 
 
        //도착지점의 중심좌표와 방향이 같다.
        if (x == dest[1].first && y == dest[1].second && dir == destDir) return cnt;
 
 
        int nx, ny;
        if (dir == 0) {
            //통나무의 현재 방향이 가로방향
 
            //U
            nx = x - 1;
            ny = y;
            //범위와 방문 체크
            if (nx >= 0 && !visit[nx][ny][dir]) {
                //이동할 위치에 나무가 있는지 체크
                if (map[nx][ny] == '0' && map[nx][ny - 1== '0' && map[nx][ny + 1== '0') {
                    visit[nx][ny][dir] = true;
                    q.push(Tree(nx, ny, dir, cnt + 1));
                }
            }
 
 
            //D
            nx = x + 1;
            ny = y;
            if (nx < n && !visit[nx][ny][dir]) {
                if (map[nx][ny] == '0' && map[nx][ny - 1== '0' && map[nx][ny + 1== '0') {
                    visit[nx][ny][dir] = true;
                    q.push(Tree(nx, ny, dir, cnt + 1));
                }
            }
 
 
            //L
            nx = x;
            ny = y - 1;
            if (ny -1 >= 0  && !visit[nx][ny][dir]) {
                if (map[nx][ny - 1== '0') {
                    visit[nx][ny][dir] = true;
                    q.push(Tree(nx, ny, dir, cnt + 1));
                }
            }
 
 
            //R
            nx = x;
            ny = y + 1;
            if (ny + 1 < n && !visit[nx][ny][dir]) {
                if (map[nx][ny + 1== '0') {
                    visit[nx][ny][dir] = true;
                    q.push(Tree(nx, ny, dir, cnt + 1));
                }
            }
 
 
            //T
            if (turn(x,y) && !visit[x][y][1]) {
                q.push(Tree(x, y, 1, cnt + 1));
                visit[x][y][1= true;
            }
 
        }
        else {
            //세로 방향
 
            //U
            nx = x - 1;
            ny = y;
            if (nx - 1 >= 0 && !visit[nx][ny][dir]) {
                if (map[nx - 1][ny] == '0') {
                    q.push(Tree(nx, ny, dir, cnt + 1));
                    visit[nx][ny][dir] = true;
                }
            }
 
 
            //D
            nx = x + 1;
            ny = y;
            if (nx + 1 < n && !visit[nx][ny][dir]) {
                if (map[nx + 1][ny] == '0') {
                    q.push(Tree(nx, ny, dir, cnt + 1));
                    visit[nx][ny][dir] = true;
                }
            }
 
 
            //L
            nx = x;
            ny = y - 1;
            if (ny >= 0 && !visit[nx][ny][dir]) {
                if (map[nx - 1][ny] == '0' && map[nx][ny] == '0' && map[nx + 1][ny] == '0') {
                    q.push(Tree(nx, ny, dir, cnt + 1));
                    visit[nx][ny][dir] = true;
                }
            }
 
 
            //R
            nx = x;
            ny = y + 1;
            if (ny < n && !visit[nx][ny][dir]) {
                if (map[nx - 1][ny] == '0' && map[nx][ny] == '0' && map[nx + 1][ny] == '0') {
                    q.push(Tree(nx, ny, dir, cnt + 1));
                    visit[nx][ny][dir] = true;
                }
            }
 
 
            //T
            if (turn(x, y) && !visit[x][y][0]) {
                q.push(Tree(x, y, 0, cnt + 1));
                visit[x][y][0= true;
            }
        }
 
    }
 
    return 0;
}
 
 
 
int main() {
    ios_base::sync_with_stdio(false);
    cin.tie(0);
 
    cin >> n;
 
 
    int k = 0;
    int l = 0;
    for (int i = 0; i < n; i++) {
        for (int j = 0; j < n; j++) {
            cin >> map[i][j];
            if (map[i][j] == 'B') {
                start[k].first = i;
                start[k].second = j;
                k++;
                map[i][j] = '0';
            }
            else if (map[i][j] == 'E') {
                dest[l].first = i;
                dest[l].second = j;
                l++;
                map[i][j] = '0';
            }
        }
    }
 
 
    //도착지점의 방향
    //첫번째와 두번째 위치의 x좌표가 같으면 가로방향
    //가로 방향 : 0 , 세로 방향 : 1
    if (dest[0].first == dest[1].first) destDir = 0;
    else destDir = 1;
 
 
 
    //시작위치의 방향
    //첫번째와 두번째 위치의 x좌표가 같으면 가로방향
    //가로 방향 : 0 , 세로 방향 : 1
    if (start[0].first == start[1].first) cout << solve(start[1].first, start[1].second, 0);
    else cout << solve(start[1].first, start[1].second, 1);
 
 
 
    return 0;
}
Colored by Color Scripter
 

'BOJ' 카테고리의 다른 글

[BOJ] 1331. 나이트 투어  (0) 2020.03.03
[BOJ] 4179. 불!  (0) 2020.02.17
[BOJ] 17780. 새로운 게임  (0) 2020.02.03
[BOJ] 17837. 새로운 게임 2  (0) 2020.02.03
[BOJ] 1600. 말이 되고픈 원숭이  (0) 2020.02.01

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

 

17780번: 새로운 게임

재현이는 주변을 살펴보던 중 체스판과 말을 이용해서 새로운 게임을 만들기로 했다. 새로운 게임은 크기가 N×N인 체스판에서 진행되고, 사용하는 말의 개수는 K개이다. 말은 원판모양이고, 하나의 말 위에 다른 말을 올릴 수 있다. 체스판의 각 칸은 흰색, 빨간색, 파란색 중 하나로 색칠되어있다. 게임은 체스판 위에 말 K개를 놓고 시작한다. 말은 1번부터 K번까지 번호가 매겨져 있고, 이동 방향도 미리 정해져 있다. 이동 방향은 위, 아래, 왼쪽, 오른쪽

www.acmicpc.net

17837번 새로운 게임 2 문제(문제 링크)와 거의 똑같은데 조금 더 쉬운 시뮬레이션 문제이다.

(문제 풀이 링크)

이 문제에서는 말이 맨 밑에 있지 않으면 이동시킬 수 없다는 것이 17837번 문제와의 차이점이다.

 

 

 

말의 정보 구조체를 만들어서 행과 열의 위치, 방향을 넣어서 벡터에 저장한다.

벡터의 i번째 위치에는 i번째 말의 위치와 방향이 저장되어 있다.

 

방향 정보는 문제에서 알려준 순서대로 오른쪽, 왼쪽, 위쪽, 아래쪽이 각각 1, 2, 3, 4번의 인덱스가 되도록 배열에 넣어놓는다. 1번부터 시작하기 위해서 0번 위치에는 그냥 0을 넣어놨다.

 

말을 이동시키기 위해서는 deque배열을 사용하였다.

deque<int> dq[13][13];

위와 같이 deque<int>를 타입으로 가지는 2차원 배열이다.

dp[x][y]에는 x행 y열 위치에 있는 말들이 순서대로 저장되어 있다.

 

 

 

이제 턴을 진행하면서 4개 이상이 쌓이는 경우가 있다면 바로 게임을 종료하고 턴 수를 리턴한다.

문제 조건에 따라 1,000보다 크거나 절대로 게임이 종료되지 않는 경우에는 -1을 출력하기 위해서 -1을 리턴한다.

각 턴에서는 1번 말부터 k번말까지 순차적으로 이동시킨다.

 

 

문제 조건에 따라 이동시키기 전에 현재 이동시킬 말이 맨 밑에 있지 않다면 이동할 수 없으므로 바로 리턴한다.

이동시킬 말이 맨 밑에 있다면 이동할 칸의 색깔에 따라서 이동하게 된다.

 

이동할 칸

  1. 범위를 벗어나거나 파란색 칸인 경우
    • 원래 좌표에서 방향을 반대로 바꾼다.
      1. 다시 한 칸을 이동할 건데 이동할 칸이 또 범위를 벗어나거나 파란색일 경우 문제 조건에 따라 방향만 바꾼 채로 더 이동하지 않는다.
      2. 그렇지 않다면 이동을 한 건데 이동할 칸이 흰색이냐 빨간색이냐에 따라 다르게 이동할 것이므로 아래 조건들(밑에 2번, 3번 조건)을 이어서 실행해주면 된다.
  2.  흰색인 경우
    • 현재 위치에 있는 말들을 순서대로 (큐의 앞부분부터) 이동할 위치로 이동시킨다.
  3. 빨간색인 경우
    • 현재 위치에 있는 말들을 반대 순서로(큐의 뒷부분부터) 이동할 위치로 이동시킨다.

 

 

 

이동을 매번 할 때마다이동한 칸에 쌓인 말이 4개 이상이 되었는지 확인하고

4개 이상이 되었다면 true를 리턴, 그렇지 않다면 false를 리턴한다.

 

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
#include <iostream>
#include <vector>
#include <deque>
using namespace std;
 
int n, k;
int map[13][13];
 
//오, 왼, 위, 아래
int dx[] = { 00,0,-1,1 };
int dy[] = { 01,-1,0,0 };
 
 
deque<int> dq[13][13];
 
struct Horse {
    int x;
    int y;
    int dir;
    Horse(int a, int b, int c) {
        x = a;
        y = b;
        dir = c;
    }
};
 
//말의 정보를 저장할 벡터
vector<Horse> vt;
 
bool move(int num) {
 
    //num번호 말의 좌표와 방향
    int x = vt[num].x;
    int y = vt[num].y;
    int dir = vt[num].dir;
 
    int nx, ny, tmp;
 
    //이동할 좌표
    nx = x + dx[dir];
    ny = y + dy[dir];
 
    //말이 맨 밑에 있지 않으면 이동할 수 없다.
    if (dq[x][y].front() != num) return false;
 
 
    //이동할 좌표가 파란색이거나 범위를 벗어나는 경우
    if (nx < 1 || ny < 1 || nx > n || ny > n || map[nx][ny] == 2) {
        if (dir == 1 || dir == 3) dir++;
        else dir--;
 
        //반대로 다시 한칸 이동
        nx = x + dx[dir];
        ny = y + dy[dir];
 
        //말의 방향 정보 업데이트
        vt[num].dir = dir;
 
 
        //다시 이동한 칸이 또 파란색이거나 범위를 벗어나면 방향만 바꾼채로 리턴
        if (nx < 1 || ny < 1 || nx > n || ny > n || map[nx][ny] == 2) {
            return false;
        }
        //그렇지 않다면 아래의 남은 코드 진행
    }
 
    //이동할 칸이 흰색인 경우
    if (map[nx][ny] == 0) {
        //현재 칸에 있는 말들을 순서대로 이동할칸으로 옮겨준다.
        while (!dq[x][y].empty()) {
            tmp = dq[x][y].front();
            dq[x][y].pop_front();
 
            dq[nx][ny].push_back(tmp);
            vt[tmp].x = nx;
            vt[tmp].y = ny;
        }
 
    }
    else if (map[nx][ny] == 1) {
    //이동할 칸이 빨간색인 경우
 
        //현재 칸에 있는 말들을 뒤쪽부터 순서대로 이동할 칸으로 옮겨준다.
        while (!dq[x][y].empty()) {
            tmp = dq[x][y].back();
            dq[x][y].pop_back();
 
            dq[nx][ny].push_back(tmp);
            vt[tmp].x = nx;
            vt[tmp].y = ny;
        }
 
    }
 
 
    //말이 4개 이상 쌓이면 true를 리턴
    if (dq[nx][ny].size() >= 4return true;
    else return false;
}
 
 
 
int turnStart() {
    int turnCnt = 1;
 
 
    while (turnCnt <= 1000) {
 
        bool flag = false;
        for (int i = 1; i <= k; i++) {
            flag = move(i);
 
            //말이 4개이상 쌓여서 true값을 리턴받았다면 게임 종료(턴 수를 리턴)
            if (flag) return turnCnt;
        }
 
        turnCnt++;
    }
 
 
    //1000번이 넘으면 -1을 리턴
    return -1;
}
 
 
 
int main() {
    ios_base::sync_with_stdio(false);
    cin.tie();
 
    cin >> n >> k;
 
    for (int i = 1; i <= n; i++) {
        for (int j = 1; j <= n; j++) {
            cin >> map[i][j];
        }
    }
 
 
    //1번부터 사용하기 위해 0번째 위치에는 쓰레기값 넣어 놓는다.
    vt.push_back(Horse(-1-1-1));
 
 
    int x, y, d;
    for (int i = 1; i <= k; i++) {
        cin >> x >> y >> d;
 
        //말의 정보를 벡터에 넣는다.
        vt.push_back(Horse(x, y, d));
 
        //말이 있는 좌표에 말의 번호를 넣는다.
        dq[x][y].push_back(i);
    }
 
    cout << turnStart() << '\n';
 
 
    return 0;
}
Colored by Color Scripter
 

'BOJ' 카테고리의 다른 글

[BOJ] 4179. 불!  (0) 2020.02.17
[BOJ] 1938. 통나무 옮기기  (0) 2020.02.12
[BOJ] 17837. 새로운 게임 2  (0) 2020.02.03
[BOJ] 1600. 말이 되고픈 원숭이  (0) 2020.02.01
[BOJ] 2532. 반도체 설계  (0) 2020.01.29

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

 

17837번: 새로운 게임 2

재현이는 주변을 살펴보던 중 체스판과 말을 이용해서 새로운 게임을 만들기로 했다. 새로운 게임은 크기가 N×N인 체스판에서 진행되고, 사용하는 말의 개수는 K개이다. 말은 원판모양이고, 하나의 말 위에 다른 말을 올릴 수 있다. 체스판의 각 칸은 흰색, 빨간색, 파란색 중 하나로 색칠되어있다. 게임은 체스판 위에 말 K개를 놓고 시작한다. 말은 1번부터 K번까지 번호가 매겨져 있고, 이동 방향도 미리 정해져 있다. 이동 방향은 위, 아래, 왼쪽, 오른쪽

www.acmicpc.net

시뮬레이션 문제이다. 어떻게 말의 정보들을 저장하고 말을 이동시킬까 고민하다가 다음과 같이 구현해봤다.

 

 

 

말의 정보구조체를 만들어서 행과 열의 위치, 방향을 넣어서 벡터에 저장한다.

벡터의 i번째 위치에는 i번째 말의 위치와 방향이 저장되어 있다.

 

방향 정보는 문제에서 알려준 순서대로 오른쪽, 왼쪽, 위쪽, 아래쪽이 각각 1, 2, 3, 4번의 인덱스가 되도록 배열에 넣어놓는다. 1번부터 시작하기 위해서 0번 위치에는 그냥 0을 넣어놨다.

 

말을 이동시키기 위해서는 deque배열을 사용하였다.

deque<int> dq[13][13];

위와같이 deque<int>를 타입으로 가지는 2차원 배열이다.

dp[x][y]에는 x행 y열 위치에 있는 말들이 순서대로 저장되어 있다.

 

(queue가 아닌 deque를 사용한 이유는 이동할 칸이 빨간색인경우 순서를 반대로 넣어주기 위해서 사용하였다.)

(효율적인 방법인지는 잘모르겠다. 채점현황을 보니 메모리를 더 사용하는 것 같은데 다른 분들 풀이도 찾아봐야겠다...)

 

 

 

이제 턴을 진행하면서 4개 이상이 쌓이는 경우가 있다면 바로 게임을 종료하고 턴 수를 리턴한다.

문제 조건에 따라 1,000보다 크거나 절대로 게임이 종료되지 않는 경우에는 -1을 출력하기 위해서 -1을 리턴한다.

각 턴에서는 1번말부터 k번말까지 순차적으로 이동시킨다.

 

 

 

이동할 칸

  1. 범위를 벗어나거나 파란색 칸인 경우
    • 원래 좌표에서 방향을 반대로 바꾼다.
      1. 다시 한칸을 이동할건데 이동할 칸이 또 범위를 벗어나거나 파란색일 경우 문제 조건에 따라 방향만 바꾼채로 더 이동하지 않는다.
      2. 그렇지 않다면 이동을 한건데 이동할 칸이 흰색이냐 빨간색이냐에 따라 다르게 이동할 것이므로 아래 조건들(밑에 2번, 3번 조건)을 이어서 실행해주면 된다.
  2.  흰색인 경우
    • 현재 이동할 말의 번호가 num이라고 했을때
    • 현재 이동할 말의 위에 있는 말들을 함께 이동시켜주기위해서 deque에서 num번 말이 나오는 이후의 말들은 모두 같이 이동시켜주면된다.
    • deque에 들어있는 말들의 개수만큼만 반복문을 진행하면서
      1. num번이 나오지 않았다면 다시 원래 위치 deque에 넣어준다.
      2. num번이 나왔거나 이전에 num번이 나왔다면 이동할 위치의 deque에 넣어준다. (이동할 때는 해당 말의 위치정보(벡터에 넣어놓은 값)을 업데이트 해주어야한다)
  3. 빨간색인 경우
    • 현재 이동할 말의 번호가 num이라고 했을때
    • 현재 이동할 말의 위에 있는 말들을 순서를 반대로 이동시켜주기 위해서 deque의 뒷부분부터 num번까지의 말들을 순차적으로 이동시켜준다.
    • deque에 들어있는 말들의 개수만큼만 반복문을 진행하면서 num이 나올때까지 이동할 위치의 deque에 넣어주고 num번이 나온다면 num번말도 이동시켜주고 반복문을 종료한다.

 

 

 

이동을 매번 할때마다 이동한 칸에 쌓인 말이 4개 이상이 되었는지 확인하고

4개 이상이 되었다면 true를 리턴, 그렇지 않다면 false를 리턴한다.

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
#include <iostream>
#include <vector>
#include <deque>
using namespace std;
 
int n, k;
int map[13][13];
 
//오, 왼, 위, 아래
int dx[] = { 0,0,0,-1,1 };
int dy[] = { 0,1,-1,0,0 };
 
 
deque<int> dq[13][13];
 
struct Horse {
    int x;
    int y;
    int dir;
    Horse(int a, int b, int c) {
        x = a;
        y = b;
        dir = c;
    }
};
 
 
//말의 정보를 저장할 벡터
vector<Horse> vt;
 
 
bool move(int num) {
 
    //num번호 말의 좌표와 방향
    int x = vt[num].x;
    int y = vt[num].y;
    int dir = vt[num].dir;
 
    int nx, ny, tmp;
    
    //이동할 좌표
    nx = x + dx[dir];
    ny = y + dy[dir];
 
 
    //이동할 좌표가 파란색이거나 범위를 벗어나는 경우
    if (nx < 1 || ny < 1 || nx > n || ny > n || map[nx][ny] == 2) {
        //방향 전환
        if (dir == 1 || dir == 3) dir++;
        else dir--;
 
        //반대로 다시 한칸 이동
        nx = x + dx[dir];
        ny = y + dy[dir];
        
 
        //말의 방향정보 업데이트
        vt[num].dir = dir;
 
 
        //다시 이동한 칸이 또 파란색이거나 범위를 벗어나면 방향만 바꾸고 리턴
        if (nx < 1 || ny < 1 || nx > n || ny > n || map[nx][ny] == 2) {
            return false;
        }
 
        //그렇지 않다면 아래남은 코드 진행
    }
    
 
    //이동할 칸이 흰색인 경우
    if (map[nx][ny] == 0) {
        bool flag = false;
 
        int size = dq[x][y].size();
        for (int i = 0; i < size; i++) {
            tmp = dq[x][y].front();
            dq[x][y].pop_front();
 
            if (tmp == num || flag) {
                //num번 말이랑 num위에 있는 말들만 이동한다.
                flag = true//ture로 바꿔주면 앞으로 뒤에 있는 말들은 같이 이동한다.
 
 
                //이동시켜준다.
                dq[nx][ny].push_back(tmp);
                
                //좌표정보 업데이트
                vt[tmp].x = nx;
                vt[tmp].y = ny;
            }
            else {
                //num번 말 아래있는 말은 다시 원래좌표에 넣어준다.
                dq[x][y].push_back(tmp);
            }
        }
 
    }
    else if (map[nx][ny] == 1) {
    //이동할 칸이 빨간색인 경우
 
        int size = dq[x][y].size();
        for (int i = 0; i < size; i++) {
            //반대 순서로 넣어줄 것이므로 뒤에것을 빼준다.
            tmp = dq[x][y].back();
            dq[x][y].pop_back();
 
 
            //이동시켜준다.
            dq[nx][ny].push_back(tmp);
 
            //좌표정보 업데이트
            vt[tmp].x = nx;
            vt[tmp].y = ny;
 
            //num번말 위에있는말들과 num번말까지만 이동시키므로 break
            if (tmp == num) break;
        }
    }
 
 
    //말이 4개 이상 쌓이면 true를 리턴
    if (dq[nx][ny].size() >= 4return true;
    else return false;
}
 
 
 
int turnStart() {
    int turnCnt = 1;
 
 
    while (turnCnt <= 1000) {
        
        bool flag = false;
 
        for (int i = 1; i <= k; i++) {
            flag = move(i);
 
            //말이 4개이상 쌓여서 true값을 리턴받았다면 게임 종료(턴 수를 리턴)
            if (flag) return turnCnt;
        }
        
        turnCnt++;
    }
 
 
    //1000번이 넘으면 -1을 리턴
    return -1;
}
 
 
 
int main() {
    ios_base::sync_with_stdio(false);
    cin.tie();
 
 
    cin >> n >> k;
    for (int i = 1; i <= n; i++) {
        for (int j = 1; j <= n; j++) {
            cin >> map[i][j];
        }
    }
 
 
    //1번부터 사용하기 위해 0번째 위치에는 쓰레기값 넣어 놓는다.
    vt.push_back(Horse(-1-1-1));
 
 
    int x, y, d;
    for (int i = 1; i <= k; i++) {
        cin >> x >> y >> d;
 
        //말의 정보를 벡터에 넣는다.
        vt.push_back(Horse(x, y, d));
 
        //말이 있는 좌표에 말의 번호를 넣는다.
        dq[x][y].push_back(i);
    }
 
 
    cout <<  turnStart() << '\n';
 
 
    return 0;
}
Colored by Color Scripter
 

'BOJ' 카테고리의 다른 글

[BOJ] 1938. 통나무 옮기기  (0) 2020.02.12
[BOJ] 17780. 새로운 게임  (0) 2020.02.03
[BOJ] 1600. 말이 되고픈 원숭이  (0) 2020.02.01
[BOJ] 2532. 반도체 설계  (0) 2020.01.29
[BOJ] 12783. 가장 긴 증가하는 부분 수열 3  (0) 2020.01.29

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

 

1600번: 말이 되고픈 원숭이

첫째 줄에 자연수 K가 주어진다. 둘째 줄에 격자판의 가로길이 W, 세로길이 H가 주어진다. 그 다음 H줄에 걸쳐 W개의 숫자가 주어지는데, 0은 아무것도 없는 평지, 1은 장애물을 뜻한다. 장애물이 있는 곳으로는 이동할 수 없다. 시작점과 도착점은 항상 평지이다. W와 H는 1이상 200이하의 자연수이고, K는 0이상 30이하의 정수이다.

www.acmicpc.net

최소 동작수를 구하는 문제이므로 bfs로 이동할 수 있는 방법을 매번 사용하여 정답을 구하면 된다.

벽 부수고 이동하기 문제와 비슷하게 풀었다.

 

 

인접한 네 방향으로 이동하는 경우와 아직 능력을 k번만큼 사용하지 않았다면 말의 움직임처럼 이동하는 경우를 모두 큐에 넣어주고 이동할 때마다 배열에 동작 수를 저장한다.

 

 

이때 해당칸에 도착할 때 능력을 몇 번 사용했는지에 따라 다르므로 삼차원 배열을 사용한다.

visit[x][y][z] = (x, y) 좌표에 능력을 z번 사용해서 온 동작의 수

 

 

마지막 visit[h-1][w-1][0]부터 visit[h-1][w-1][k]까지 중 최솟값이 정답이다.

(마지막 칸에 도착했을때 능력을 0번 사용한 경우부터 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
117
118
119
#include <iostream>
#include <vector>
#include <queue>
#include <algorithm>
#include <cstring>
using namespace std;
 
int k, w, h;
int map[200][200];
 
int dx[] = { 0,0,1,-1 };
int dy[] = { 1,-1,0,0 };
 
int hx[] = { -2-2-1-11122 };
int hy[] = { -11-22-22-11 };
 
int visit[200][200][31];
 
 
struct Pos {
    int x;
    int y;
    int cnt; //능력을 사용한 횟수
    Pos(int a, int b, int c) {
        x = a;
        y = b;
        cnt = c;
    }
};
 
 
void bfs(int x, int y) {
    memset(visit, -1sizeof(visit));
 
    queue<Pos> q;
    q.push(Pos(x, y, 0));
    visit[x][y][0= 0;
 
    int cnt, nx, ny;
    while (!q.empty()) {
        x = q.front().x;
        y = q.front().y;
        cnt = q.front().cnt;
        q.pop();
 
 
        //능력을 아직 사용할 수 있어서 말처럼 움직이는 경우
        if (cnt < k) {
 
            for (int i = 0; i<8; i++) {
                nx = x + hx[i];
                ny = y + hy[i];
 
                //범위 체크
                if (nx < 0 || ny < 0 || nx >= h || ny >= w) continue;
                //장애물 확인
                if (map[nx][ny] == 1continue;
                //방문확인
                if (visit[nx][ny][cnt + 1!= -1continue;
 
 
                //능력을 사용하였으므로 cnt를 증가시킨 채로 큐에 넣는다.
                q.push(Pos(nx, ny, cnt + 1));
                visit[nx][ny][cnt + 1= visit[x][y][cnt] + 1;
            }
        }
 
 
        //인접한 칸으로 이동하는 경우
        for (int i = 0; i<4; i++) {
            nx = x + dx[i];
            ny = y + dy[i];
 
            if (nx < 0 || ny < 0 || nx >= h || ny >= w) continue;
            if (map[nx][ny] == 1continue;
            if (visit[nx][ny][cnt] != -1continue;
 
 
            //능력을 사용하지 않은 경우이므로 cnt는 그대로이다
            q.push(Pos(nx, ny, cnt));
            visit[nx][ny][cnt] = visit[x][y][cnt] + 1;
 
        }
 
 
    }
 
}
 
int main() {
    ios_base::sync_with_stdio(false);
    cin.tie(0);
 
    cin >> k;
    cin >> w >> h;
 
 
    for (int i = 0; i<h; i++) {
        for (int j = 0; j<w; j++) {
            cin >> map[i][j];
        }
    }
 
 
    bfs(00);
 
    int ans = 2100000000;
 
    for (int i = 0; i <= k; i++) {
        if (visit[h - 1][w - 1][i] != -1 && ans > visit[h - 1][w - 1][i]) {
            ans = visit[h - 1][w - 1][i];
        }
    }
 
    if (ans == 2100000000cout << -1 << '\n';
    else cout << ans << '\n';
 
 
    return 0;
}
Colored by Color Scripter
 

+ Recent posts