문제
러시아 가스를 크로아티아로 운반하기 위해 자그레브와 모스코바는 파이프라인을 디자인하고 있다. 두 사람은 실제 디자인을 하기 전에 파이프 매니아 게임을 이용해서 설계를 해보려고 한다.
이 게임에서 유럽은 R행 C열로 나누어져 있다. 각 칸은 비어있거나, 아래 그림과 같은 일곱가지 기본 블록으로 이루어져 있다.
파이프 라인의 설계를 마친 후 두 사람은 잠시 저녁을 먹으러 갔다. 그 사이 해커가 침임해 블록 하나를 지웠다. 지운 블록은 빈 칸이 되어있다.
해커가 어떤 칸을 지웠고, 그 칸에는 원래 어떤 블록이 있었는지 구하는 프로그램을 작성하시오.
입력
첫째 줄에 유럽의 크기 R과 C가 주어진다. (1 ≤ R, C ≤ 25)
다음 R개 줄에는 C개 글자가 주어지며, 다음과 같은 글자로 이루어져 있다.
- 빈칸을 나타내는 '.'
- 블록을 나타내는 '|'(아스키 124), '-','+','1','2','3','4'
- 모스크바의 위치를 나타내는 'M'과 자그레브를 나타내는 'Z'. 두 글자는 한 번만 주어진다.
항상 답이 존재하고, 가스의 흐름이 유일한 경우만 입력으로 주어진다, 또, 모스크바와 자그레브가 하나의 블록과 인접해 있는 입력만 주어진다. 또, 불필요한 블록이 존재하지 않는다. 즉, 없어진 블록을 추가하면, 모든 블록에 가스가 흐르게 된다.
출력
지워진 블록의 행과 열 위치를 출력하고, 어떤 블록이었는지를 출력한다.
풀이
- 지워진 블록은 4방향중 2방향 이상에서 해당 블록으로 접근을 시도한다. 따라서 . 블록중 1블록은 2개 또는 4개 방향에서 연결 시도가 오는 블록이다.
- 예외케이스로 Z또는 M에 연결된 가스관은 1개 또는 3개 방향에서 연결 시도를 한다. 이를 고려해서 (0,0)부터 (n-1, n-1)까지 순차적으로 확인하는데 주변에 해당 블록으로 방향이 되어있는 가스관이 없다면 다음 블록검증으로 넘어간다.
- 과정은 아래와 같다
그렇다면 해당 .칸에서 주변과 연결가능한 블록이 몇개있는지는 어떻게 파악 가능할까?
아래와 같이 칸당 연결 불가능한 블록이 3개씩 존재한다. 이것을 HashSet<Character> 에 저장해주면 된다.
각 방향 4개당 연결 불가능한 블록이 다르기때문에 배열을 만들어준다. HashSet<Charcter>[] 이렇게 말이다.
이후에 연결된 통로가 0개면 continue; 4개면 '+' 2개라면 해당 방향에 따라서 '|', '-', '1, ',2', '3', '4' 중에 선택해서 넣으면 된다. 추가로 M과 Z근처의 블록이 지워졌을 수도 있다. 이럴경우 연결된 통로가 1이거나 3이기때문에 이런 홀수 처리도 진행해야한다.
예제 입력 예시
3 7
.......
.M-.-Z.
.......
예제 출력 예시
2 4 -
소스코드
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.Arrays;
import java.util.HashSet;
import java.util.StringTokenizer;
public class Main {
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
StringTokenizer st = new StringTokenizer(br.readLine());
StringBuilder sb = new StringBuilder();
int R = Integer.parseInt(st.nextToken()); //행
int C = Integer.parseInt(st.nextToken()); //열
char[][] map = new char[R][C];
boolean[] visited = new boolean[4];
int[][] dydx = {{0, -1}, {-1, 0}, {0, 1}, {1, 0}};
for(int i = 0; i < R; i++) {
char[] charArr = br.readLine().toCharArray();
for(int j = 0; j < C; j++) {
if(charArr[j] == 'M'||charArr[j] == 'Z') {
map[i][j] = '*';
}
else map[i][j] = charArr[j];
}
}
//규칙 추가, 좌 상 우 하 기준 0~4, 피해야하는 값 추가
HashSet<Character>[] setArr = new HashSet[4];
for(int i = 0; i < 4; i++) {
HashSet<Character> set = new HashSet<>();
set.add('.');
set.add('*');
if(i%2==0) set.add('|');
else set.add('-');
if(i == 0) {
set.add('3');
set.add('4');
}else if(i == 1) {
set.add('2');
set.add('3');
}else if(i == 2) {
set.add('1');
set.add('2');
}else if(i == 3) {
set.add('1');
set.add('4');
}
setArr[i] = set;
}
loop:for(int i = 0; i < R; i++) {
for(int j = 0; j < C; j++) {
if(map[i][j] != '.') continue;
Arrays.fill(visited, false);
int cnt = 0;
for(int k = 0; k < 4; k++) {
int cy = i+dydx[k][0];
int cx = j+dydx[k][1];
//set에 없는 값이라면 연결되야하는 통로!
if(cy>=0&&cy<R&&cx>=0&&cx<C&&!setArr[k].contains(map[cy][cx])) {
visited[k] = true;
cnt++;
}
}
if(cnt == 0) continue;
else if(cnt == 1 || cnt == 3) {
for(int k = 0; k < 4; k++) {
int cy = i+dydx[k][0];
int cx = j+dydx[k][1];
//set에 없는 값이라면 연결되야하는 통로!
if(cy>=0&&cy<R&&cx>=0&&cx<C&&map[cy][cx] == '*') {
visited[k] = true;
cnt++;
break;
}
}
}
// 맞는 통로 찾기
sb.append(i+1).append(" ").append(j+1).append(" ");
if(cnt == 4) sb.append('+');
else if(visited[1]&&visited[3]) sb.append('|');
else if(visited[0]&&visited[2]) sb.append('-');
else if(visited[2]&&visited[3]) sb.append('1');
else if(visited[1]&&visited[2]) sb.append('2');
else if(visited[0]&&visited[1]) sb.append('3');
else if(visited[0]&&visited[3]) sb.append('4');
sb.append("\n");
break loop;
}
}
System.out.println(sb);
}
}
문제 출처
https://www.acmicpc.net/problem/2931
'알고리즘 > 자바' 카테고리의 다른 글
[백준 알고리즘] 2357번 자바(Java) 최솟값과 최댓값 (0) | 2024.10.15 |
---|---|
[백준 알고리즘] 17070번 자바(Java) 파이프 옮기기 1 (0) | 2024.09.06 |
[백준 알고리즘] 12100번 자바(Java) 2048(Easy) (0) | 2024.08.22 |
[SWEA] 6109번 자바(Java) 추억의 2048게임 (0) | 2024.08.22 |
[백준 알고리즘] 12015번 자바(Java) 가장 긴 증가하는 부분 수열2 (0) | 2024.08.17 |