본문 바로가기
PS 문제 풀이/Baekjoon

[BOJ] 백준 17822 원판 돌리기

by 까망 하르방 2021. 2. 22.
반응형

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

 Input 

4 4 3

1 1 2 3

5 2 4 2

3 1 3 5

2 1 3 2

2 0 1

3 1 3

2 0 2

 

 Output 

22

시뮬레이션

각각의 원에 일정한 간격으로 M(=4)개의 숫자들이 배치되어 있습니다.

특정 좌표 (i, j)에서 인접한 좌표는 (i-1, j) / (i+1, j) / (i, j-1) / (i, j+1)에 해당합니다.

 

▶ (2, 0, 1) = 2, 4번 원판을 시계 방향으로 1칸 회전.

인접한 좌표간에 동일한 숫자가 존재하기에 해당 수들을 모두 지웁니다. 

(지워지지 않은 숫자들의 이동은 없습니다.)

→ 각 원판의 숫자들의 합 = 30

 

 

▶ (3, 1, 3) = 3번 원판을 반시계 방향으로 3칸 회전.

인접한 좌표간에 동일한 숫자가 존재하기에 해당 수들을 모두 지웁니다.

→ 각 원판의 숫자들의 합 = 22

 

 

▶ (2, 0, 2) = 2, 4번 원판을 시계 방향으로 2칸 회전

인접한 좌표간에 동일한 숫자가 없으므로, 지워지지 않은 숫자들의 평균을 구합니다.

평균값(= 22/6 = 3.x)보다 큰 수는 -1 / 작은수는 +1 처리

→ 각 원판의 숫자들의 합 = 22

 

구현

① 원판의 적혀있는 숫자를 이차원 배열로 구현

     → int[][] disk

② 회전 정보에 따라 시계/반시계 방향 구현

     - xi의 배수에 해당하는 원판을 모두 독립적으로 회전

        ex) 2의 배수 = 2, 4, 6, 8, 10...

 

③ 인접한 좌표간 동일한 숫자 존재 여부 확인

     - 접한 좌표를 판단할 때, 끝자리와 처음자리가 인접하는 것으로 처리.

     - BFS or DFS를 통해 어느 범위까지 동일한지 확인

  

④ 인접한 좌표간 동일한 숫자 존재 여부에 따른 처리

    - 존재하는 경우 삭제되었다는 의미로 0으로 대체

    - 존재하지 않는 경우 평균값에 따른 숫자 갱신

       평균의 경우 소숫점 처리  double형

 

※ 회전만 구현했을 때의 결과

 

※ 회전 + 인접한 좌표간 동일한 숫자 존재 여부에 따른 처리


import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.Queue;
import java.util.StringTokenizer;

public class Main {	
	static int N, M, T;
	static int x, d, k;
	static int[][] disk;
	static int[] dr = {-1, 1, 0, 0};
	static int[] dc = {0, 0, -1, 1};
	static boolean[][] visited;
	static Queue<Node> queue;
	static List<Node> list;
	
    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new   InputStreamReader(System.in));
        StringTokenizer st = null;
        
        st = new StringTokenizer(br.readLine());
        N = Integer.parseInt(st.nextToken());
        M = Integer.parseInt(st.nextToken());
        T = Integer.parseInt(st.nextToken());
        
        disk = new int[N+1][M+1];
        for(int i=1; i<=N; i++) {
        	st = new StringTokenizer(br.readLine());
        	for(int j=1; j<=M; j++) {
        		disk[i][j] = Integer.parseInt(st.nextToken());
        	}
        }
        
        while(T-- > 0) { // 회전 정보 개수
        	st = new StringTokenizer(br.readLine());
        	// 회전 원판 (x의 배수)
        	x = Integer.parseInt(st.nextToken());
        	// 회전 방향(시계(0), 반시계(1))
        	d = Integer.parseInt(st.nextToken());
        	// 회전 크기
        	k = Integer.parseInt(st.nextToken());
        	
        	// x 배수에 해당하는 원판들을 독립적으로 회전
        	for(int order=x; order<=N; order = order+x) {
        		rotate(order);
        	}
        	// 인접한 좌표간 동일한 숫자가 있는지 확인
        	if(!findSameNumber()) {
        		// 인접한 곳에 동일한 숫자가 발견되지 않은 경우
        		// 평균에 따른 처리
        		actionByAvg();
        	}
        }
        // 정답출력
        printAnswer();
    }
     
    private static void actionByAvg() {
		double sum = 0;
		double cnt = 0;
		double avg = 0;
		// 평균값 구하기
		for(int r=1 ; r <= N ; ++r) {
			for(int c=1 ; c <= M ; ++c) {
				if(disk[r][c] > 0) {
					sum += disk[r][c];
					cnt++;
				}
			}
		}
		if(cnt == 0) return;
		avg = sum / cnt;
		
		// 평균값을 기준으로 지워지지 않은 숫자에 대한 처리
		for(int r = 1 ; r <= N ; ++r) {
			for(int c = 1 ; c <= M ; ++c) {
				if(disk[r][c] == 0) continue;
				if(disk[r][c] < avg) disk[r][c] += 1;
				else if(disk[r][c] > avg) disk[r][c] -= 1;
			}
		}
	}

	private static boolean findSameNumber() {
		boolean isSameNum = false;
		visited = new boolean[N+1][M+1];
		queue = new LinkedList<Node>();
		// 인접한 좌표간 동일한 숫자들이 몇개 있는지 확인하는 리스트
		list = new ArrayList<Node>();
		// 모든 영역에 대해 BFS 탐색
		for(int r=1 ; r <= N ; r++) {
			for(int c=1 ; c <= M ; c++) {
				// 이미 방문한 곳이거나 제거된 곳이면 continue
				if(disk[r][c] == 0 || visited[r][c]) continue;
				Node current = new Node(r, c);
				list.add(current);
				queue.offer(current);
				
				// 기준값 전달
				BFS(disk[r][c]);
				// BFS결과 인접한 곳에 동일한 숫자가 2개 이상 있다면
				if(list.size() > 1) {
					isSameNum = true;
					for(Node node : list) {
						// 원판에서 숫자 제거
						disk[node.r][node.c] = 0;
					}
				}
				list.clear();
			}
		}
		return isSameNum;
	}

	private static void BFS(int targetNum) {
		while(!queue.isEmpty()) {
			Node current = queue.poll();
			
			for(int i = 0 ; i < 4 ; ++i) {
				int nR = current.r + dr[i];
				int nC = current.c + dc[i];
				
				// 한 행에서 양쪽 끝은 원판에서 인접한 좌표 
				if(nC > M) nC = 1;
				else if(nC < 1) nC = M;
				
				// 범위를 벗어나거나 방문한 곳이면 continue
				if(nR > N || nR < 1 || visited[nR][nC]) continue;
				
				// 인접한 좌표가 같은 숫자인 경우
				if(disk[nR][nC] == targetNum) {
					visited[nR][nC] = true;
					Node next = new Node(nR, nC);
					queue.offer(next);
					list.add(next);
				}
			}
		}
	}

	private static void rotate(int order) {
    	// 주어진 회전 크기만큼 원판 회전시킵니다.
		for(int cnt=1; cnt<=k; cnt++) {
			// 시계 방향
			if(d == 0) { 
		    	int temp = disk[order][M];
		    	for(int i=M; i>1; i--) {
		    		disk[order][i] = disk[order][i-1];
		    	}
		    	disk[order][1] = temp;
	    	}
			// 반시계 방향
	    	else { 
	    		int temp = disk[order][1];
		    	for(int i=1; i<M; i++) {
		    		disk[order][i] = disk[order][i+1];
		    	}
		    	disk[order][M] = temp;
	    	}
		}
	}

	public static void printAnswer() {
		int sum = 0;
        StringBuilder sb = new StringBuilder();
        for(int i=1; i<=N; i++) {
        	for(int j=1; j<=M; j++) {
        		sum += disk[i][j];
        	}
        }
        sb.append(sum);
        System.out.println(sb.toString());
    }
}

class Node{
	int r, c;
	Node(int r, int c){
		this.r = r;
		this.c = c;
	}
}
반응형

'PS 문제 풀이 > Baekjoon' 카테고리의 다른 글

[BOJ] 백준 2563 색종이  (0) 2021.02.22
[BOJ] 백준 17825 주사위 윷놀이  (0) 2021.02.22
[BOJ] 백준 5373 큐빙  (0) 2021.02.22
[BOJ] 백준 15686 치킨배달  (0) 2021.02.22
[BOJ] 백준 17837 새로운 게임 2  (0) 2021.02.22

댓글