본문 바로가기
algorithm/SW Expert Academy

[SWEA] 4013.특이한 자석 / 시뮬레이션

by buddev 2020. 1. 9.

@시뮬레이션 | 1h

백준 14891.톱니바퀴와 똑같은 문제

어려운 문제는 아니고, 그냥 차근차근 풀어가면 되는 문제다.

인덱스 관리도 약간 있어서 처음에 시뮬 연습하기에 좋은 문제인 듯.

근데 문제 잘못 이해해서, 로직을 잘못 짜서 고치느라 시간이 좀 걸렸다.

 

 

문제 링크

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

 

SW Expert Academy

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

swexpertacademy.com

 

구현 방법

이번 문제는 재귀 식으로 만약 3번의 왼쪽이 돌아가면, 그 함수에서 다시 왼쪽을 검사해서 돌리는 식으로 재귀적으로 풀었다.

왜냐면.. 순차적으로 돌아가는 줄 알고ㅠ

근데 재귀는 익숙하지도 않고, 혹시나 내가 예상하지 못한 결과가 나올까봐 좀 불안했다.

 

예전에 백준 톱니바퀴를 풀었을 때에는 그냥 왼쪽 반복문, 오른쪽 반복문으로 한번에 체크했는데 이 방식이 훨씬 깔끔하고 나은 것 같다.

문제를 처음부터 제대로 이해했으면 이렇게 풀었을 것 같다.

맞은 후 다시 반복문으로 고쳐서 제출해봤는데, 시간은 엇비슷하지만 코드가 훨씬 짜기 쉬웠다

 

 

구현 포인트

1. 톱니바퀴 수만큼의 int 배열을 만들고, check[돌아야 하는 톱니바퀴 번호] = 돌아야 하는 방향; 으로 저장한다.

재귀든, 반복문이든 돌아야 할 톱니바퀴 번호를 다 체크한 후 for문을 돌면서 check 배열 값이 0이 아닌 경우에 회전하도록 구현했다.

이 때, 회전한 후에 꼭 check 배열 값을 0으로 초기화 해주는 작업이 필요하다.

 

2. 맞닿아 있는 톱니바퀴는 회전 방향이 반대이다. 1과 -1로 변하기 때문에 -1을 곱해줘서 방향을 반대로 바꿔줬다.

 

 

 

반복문을 사용한 코드 (개인적으로 이게 더 낫다고 생각한다. 구현하기에도 + 읽기에도)

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.StringTokenizer;

public class Solution_while {
	private static int k, map[][], dir[][], rotateCheck[];
	public static void main(String[] args) throws IOException {
		BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
		StringTokenizer st;
		StringBuilder sb = new StringBuilder();
		
		int T = Integer.parseInt(br.readLine());
		for (int t = 1; t <= T; t++) {
			rotateCheck = new int[4];
			
			k = Integer.parseInt(br.readLine());
			dir = new int[k][2];
			
			map = new int[4][8];
			for (int i = 0; i < 4; i++) {
				st = new StringTokenizer(br.readLine());
				for (int j = 0; j < 8; j++)
					map[i][j] = Integer.parseInt(st.nextToken());
			}
			
			for (int i = 0; i < k; i++) {
				st = new StringTokenizer(br.readLine());
				dir[i][0] = Integer.parseInt(st.nextToken()) - 1;	//자석 번호. 0-3으로 받기 
				dir[i][1] = Integer.parseInt(st.nextToken());		//자석 회전 방향 
			}
			
			for (int i = 0; i < k; i++) {
				rotationCheck(dir[i][0], dir[i][1]);
				rotate();
			}
			sb.append("#" + t + " " + getScore() + "\n");
		}
		System.out.println(sb);
	}
	
	private static void rotationCheck(int idx, int direction) {
		rotateCheck[idx] = direction;
		
		int tempIdx = idx;
		int reverseDir = -1 * direction;
		
		while (tempIdx >= 1) {	//왼쪽에 자석이 있을 경우에
			if (map[tempIdx][6] != map[tempIdx - 1][2]) {
				rotateCheck[tempIdx - 1] = reverseDir;
				tempIdx--;
				reverseDir *= -1;
			} else {
				break;
			}
		}
		
		tempIdx = idx;
		reverseDir = -1 * direction;
		while (tempIdx < 3) {	//오른쪽에 자석이 있을 경우에
			if (map[tempIdx][2] != map[tempIdx + 1][6]) {
				rotateCheck[tempIdx + 1] = reverseDir;
				tempIdx++;
				reverseDir *= -1;
			} else {
				break;
			}
		}
	}
		
	private static void rotate() {
		for (int i = 0; i < 4; i++) {
			if (rotateCheck[i] == 0) continue;
			
			if (rotateCheck[i] == 1) {	//시계방향 
				int temp = map[i][7];
				for (int j = 7; j >= 1; j--)
					map[i][j] = map[i][j - 1];
				map[i][0] = temp;
			
			} else {					//반시계방향 
				int temp = map[i][0];
				for (int j = 0; j < 7; j++)
					map[i][j] = map[i][j + 1];
				map[i][7] = temp;
			}
			
			rotateCheck[i] = 0;			//값 꼭 초기화 해주기! 
		}
	}
	
	private static int getScore() {
		int score = 0;
		for (int i = 0; i < 4; i++) {
			if (map[i][0] == 1)
				score += Math.pow(2, i);
		}
		return score;
	}
}

 

 

재귀를 사용한 코드

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.StringTokenizer;

public class Solution_recursion {
	private static int k, map[][], dir[][], rotateCheck[];
	public static void main(String[] args) throws IOException {
		BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
		StringTokenizer st;
		StringBuilder sb = new StringBuilder();
		
		int T = Integer.parseInt(br.readLine());
		for (int t = 1; t <= T; t++) {
			rotateCheck = new int[4];
			
			k = Integer.parseInt(br.readLine());
			dir = new int[k][2];
			
			map = new int[4][8];
			for (int i = 0; i < 4; i++) {
				st = new StringTokenizer(br.readLine());
				for (int j = 0; j < 8; j++)
					map[i][j] = Integer.parseInt(st.nextToken());
			}
			
			for (int i = 0; i < k; i++) {
				st = new StringTokenizer(br.readLine());
				dir[i][0] = Integer.parseInt(st.nextToken()) - 1;	//자석 번호. 0-3으로 받기 
				dir[i][1] = Integer.parseInt(st.nextToken());		//자석 회전 방향 
			}
			
			for (int i = 0; i < k; i++) {
				rotationCheck(dir[i][0], dir[i][1], true);
				rotationCheck(dir[i][0], dir[i][1], false);
				rotateCheck[dir[i][0]] = dir[i][1];
				rotate();
			}
			sb.append("#" + t + " " + getScore() + "\n");
		}
		System.out.println(sb);
	}
	
	private static void rotationCheck(int idx, int direction, boolean isLeft) {
		int reverseDir = -1 * direction;
		
		if (isLeft) {
			if (idx >= 1) {	//왼쪽에 자석이 있을 경우에
				if (map[idx][6] != map[idx - 1][2]) {
					rotationCheck(idx - 1, reverseDir, true);
					rotateCheck[idx - 1] = reverseDir;
				}
			}
			
		} else {
			if (idx < 3) {
				if (map[idx][2] != map[idx + 1][6]) {
					rotationCheck(idx + 1, reverseDir, false);
					rotateCheck[idx + 1] = reverseDir;
				}
			}
		}
	}
		
	private static void rotate() {
		for (int i = 0; i < 4; i++) {
			if (rotateCheck[i] == 0) continue;
			
			if (rotateCheck[i] == 1) {	//시계방향 
				int temp = map[i][7];
				for (int j = 7; j >= 1; j--)
					map[i][j] = map[i][j - 1];
				map[i][0] = temp;
			
			} else {					//반시계방향 
				int temp = map[i][0];
				for (int j = 0; j < 7; j++)
					map[i][j] = map[i][j + 1];
				map[i][7] = temp;
			}
			
			rotateCheck[i] = 0;			//값 꼭 초기화 해주기! 
		}
	}
	
	private static int getScore() {
		int score = 0;
		for (int i = 0; i < 4; i++) {
			if (map[i][0] == 1)
				score += Math.pow(2, i);
		}
		return score;
	}
}

 

위가 반복문으로 다시 푼 코드, 아래가 재귀로 푼 코드

 

 

삽질 포인트

1번 톱니바퀴와 2번 톱니바퀴가 돌면, 돌고 나서 3번 톱니바퀴가 도는 줄 알고 코드를 짰다가 틀렸다.

맞물려서 하나씩 순서대로 도는게 아니라

한 시점을 기준으로 전체 접합부를 확인한 후 한꺼번에 돌린다.

 

끝!

댓글