8 분 소요

1. 문제 접근 방법 및 아이디어

이 문제의 핵심 아이디어는 영향력이 전파되는 방향과 실제 회전 처리를 분리하는 것 입니다.

  1. 방향성 확인
    • 특정 자석이 회전할 때, 왼쪽과 오른쪽으로 가며 인접한 자석들과의 접점(맞닿은 극)이 서로 다른지 확인하여 회전 여부를 결정합니다.
  2. 독립적 결정
    • 자석 A가 돌아서 B가 도는 것이 아니라, A가 돌기 전의 상태를 기준으로 B의 회전 여부가 결정됩니다.
    • 따라서 모든 자석의 회전 방향을 먼저 결정한 뒤 한꺼번에 돌려야 합니다.
  3. 회전 처리
    • 8개의 날을 가진 자석을 시계/반시계 방향으로 회전시키는 배열 인덱스 조작이 필요합니다.
  4. 점수 계산
    • 모든 회전 명령이 끝난 뒤 1번 날의 값을 합산합니다.

2. 필요한 개념 및 알고리즘

  1. 시뮬레이션
    • 문제에 제시된 조건을 그대로 코드로 구현해야 합니다.
    • 최적의 해를 찾는 것이 아니라, 정해진 명령을 수행한 후의 최종 상태를 묻고 있기 때문입니다.
  2. 재귀
    • 현재 자석에서 좌/우로 뻗어나가며 반응을 확인하기 위해 필요합니다.
    • “하나의 자석이 회전할 때 인접한 자석이 극이 다르면.. 연쇄적으로 회전한다”라는 문구 자체가 재귀적 정의를 포함하고 있습니다.
  3. 배열 및 인덱스 조작
    • 자석의 8개 날을 관리하고 회전시키는데 필수적입니다.

3. 어려웠던 포인트

  1. 회전 시점의 딜레마
    • 이 문제에서는 이웃을 확인하기 전의 내 상태 기준으로 이웃의 회전 여부를 결정해햐 하므로, 반드시 이웃 확인 로직이 배열 회전 로직보다 먼저 나와야 합니다.
  2. 무한 재귀
    • 현재 자석이 어느 방향에서 왔는지(오른쪽/왼쪽)를 체크하지 않으면 이미 확인한 자석을 다시 확인하여 무한 루프에 빠집니다.

4. 코드 풀이

4.1 자석 회전 메인 로직

// 자석의 회전 정보 입력
for (int i = 0; i < K; i++) {
    // 자석의 번호와 방향 입력
    st = new StringTokenizer(br.readLine());
    int n = Integer.parseInt(st.nextToken());
	int d = Integer.parseInt(st.nextToken());
 
	// 1 ~ 4번 자석 방향 체크 초기화
    diretion = new int[5];
                 
    // 방향 체크
    process(n,d);
 
    // 회전 진행
    for (int j = 1; j <= 4; j++) {
        if(diretion[j] != 0) 
            rotate(j,diretion[j]);
    }
                 
} // 입력 종료
  • 준비: 회전 방향 배열을 초기화
  • 탐색: 연쇄 회전 여부 판별
  • 실행: 결저오딘 방향으로 실제 회전

4.2 자석 연쇄 회전 결정 함수

4.2.1 현재 자석의 방향 설정

// 회전 방향 저장
diretion[id] = dir;
  • 현재 자석이 어느 방향으로 회전할지 저장합니다.
  • 중복 탐색을 방지하는 방문체크 역할도 겸합니다.

4.2.2 왼쪽 탐색 코드

if (id > 1 && diretion[id - 1] == 0) {
    if (magne[id][6] != magne[id - 1][2]) {
        process(id - 1, -dir);
    }
}
  • if (id > 1 && direction[id - 1] == 0)은 탐색 가능 조건을 체크합니다.
    • id>1은 현재 자석이 1번이면 그 왼쪽에는 더 이상 자석이 없다는 의미입니다.
    • direction[id -1]은 회전 방향이 결정되지 않았는지 확인합니다.
    • 만약 이미 방향이 결정된 자석을 다시 체크하면 무한 루프에 빠지기 때문입니다.
  • if (magne[id][6] != magne[id - 1][2])은 자성 비교를 합니다.
    • 문제에서 나온 입접한 인덱스가 서로 다르면 왼쪽 자석을 반대 방향 회전시킵니다.

4.2.3 오른쪽 탐색 코드

 if (id < 4 && diretion[id + 1] == 0) {
    if (magne[id][2] != magne[id + 1][6]) {
    	process(id + 1, -dir);
    }
}
  • if (id < 4 && direction[id + 1] == 0)은 탐색 가능 조건을 체크합니다.
    • id<4은 현재 자석이 4번이면 그 오른쪽에는 더 이상 자석이 없다는 의미입니다.
    • direction[id +1]은 회전 방향이 결정되지 않았는지 확인합니다.
    • 만약 이미 방향이 결정된 자석을 다시 체크하면 무한 루프에 빠지기 때문입니다.
  • if (magne[id][2] != magne[id + 1][6])은 자성 비교를 합니다.
    • 문제에서 나온 입접한 인덱스가 서로 다르면 오른쪽 자석을 반대 방향 회전시킵니다.

4.2.4 process 함수 전체 코드

// 이웃한 자석이 회전 가능한지 체크하는 함수
static void process(int id, int dir) {
 
    // 회전 방향 저장
    diretion[id] = dir;
 
    // 왼쪽 탐색
    // 왼쪽에 회전할 자석이 존재하고, 회전 체크가 안될 경우 진행
    // id가 1이면 더 이상 탐색할 자석이 없기 때문
    // 회전 체크하는 이유는 재귀할 때 이미 체크한 곳을 다시 확인하는 것을 방지
    if (id > 1 && diretion[id - 1] == 0) {
 
        // 자신의 6번과 오른쪽의 2번이 붙어 있는 자석이다.
        // 붙어 있는 자석이 서로 다르면 반대 방향으로 1칸 회전한다.
        if (magne[id][6] != magne[id - 1][2]) {
            process(id - 1, -dir);
        }
 
    }
 
    // 오른쪽 탐색
    // 오른쪽에 회전할 자석이 존재하고, 회전 체크가 안될 경우 진행
    // id가 4이면 더 이상 탐색할 자석이 없기 때문
    // 회전 체크하는 이유는 재귀할 때 이미 체크한 곳을 다시 확인하는 것을 방지
    if (id < 4 && diretion[id + 1] == 0) {
 
        // 자신의 2번과 왼쪽 6번이 붙어 있는 자석이다.
        // 붙어 있는 자석이 서로 다르면 반대 방향으로 1칸 회전한다.
        if (magne[id][2] != magne[id + 1][6]) {
            process(id + 1, -dir);
        }
    }
 
} // 체크 함수 종료

4.3 roate 함수

4.3.1 시계 방향 회전

// 시계 방향 회전
// 오른쪽 시프트 연산
// 맨 뒤의 원소 추출
int temp = magne[id][7];
             
// 원소 오른쪽으로 이동
for (int i = 7; i > 0; i--) {
    magne[id][i] = magne[id][i-1];
}
             
// 첫번째 원소 갱신
magne[id][0] = temp;
  • 시계 방향 회전은 오른쪽 시프트 연산이라고 생각하면 됩니다.
  • 오른쪽 시프트 연산할 때 배열의 마지막 원소는 사라질 수 있기 때문에 미리 임시로 추출합니다.
  • 배열의 뒤에서부터 순회하며 앞의 값을 뒤로 옯깁니다. (i번째에 i-1의 값을 대입)
  • 비어있는 맨 앞자리(magne[id][0])에 임시로 추출한 값을 넣습니다.

4.3.2 반시계 방향 회전

// 반시계 회전
 // 왼쪽 시프트 연산
// 맨 앞에 원소 추출
int temp = magne[id][0];
             
// 원소 왼쪽으로 이동
for (int i = 0; i < 7; i++) {
    magne[id][i] = magne[id][i+1];
}
             
// 마지막 원소 갱신
magne[id][7] = temp;

  • 반시계 방향 회전은 왼쪽 시프트 연산이라고 생각하면 됩니다.
  • 왼쪽 시프트 연산할 때 배열의 첫번째 원소는 사라질 수 있기 때문에 미리 임시로 추출합니다.
  • 배열의 앞에서부터 순회하며 앞의 값을 뒤로 옯깁니다. (i번째에 i+1의 값을 대입)
  • 비어있는 맨 뒷자리(magne[id][7])에 임시로 추출한 값을 넣습니다.

4.3.3 rotate 함수 전체 코드

// 회전하는 함수
static void rotate(int id, int dir) {
    // 시계 방향 회전
    // 오른쪽 시프트 연산
    if(dir==1) {
    	// 맨 뒤의 원소 추출
        int temp = magne[id][7];
             
        // 원소 오른쪽으로 이동
        for (int i = 7; i > 0; i--) {
            magne[id][i] = magne[id][i-1];
        }
             
        // 첫번째 원소 갱신
        magne[id][0] = temp;
             
    }
    // 반시계 회전
    // 왼쪽 시프트 연산
    else {
             
        // 맨 앞에 원소 추출
        int temp = magne[id][0];
             
        // 원소 왼쪽으로 이동
        for (int i = 0; i < 7; i++) {
            magne[id][i] = magne[id][i+1];
        }
             
        // 마지막 원소 갱신
        magne[id][7] = temp;
    }
         
 
} // 회전 함수 종료

4.4

// 점수를 계산하는 함수
static int getScore() {
    int score = 0;
 
    if (magne[1][0] != 0)
        score += 1;
    if (magne[2][0] != 0)
        score += 2;
    if (magne[3][0] != 0)
        score += 4;
    if (magne[4][0] != 0)
        score += 8;
 
    return score;
}
  • 1번 ~ 4번까지 자석의 첫번째 원소가 1이면 각각 1, 2, 4, 8점을 준다.

4.5 전체 코드

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.StringTokenizer;
 
public class Solution {
 
    static int[][] magne = new int[5][8]; // 자성 정보
    static int diretion[]; // 자석의 방향 모음
 
    public static void main(String[] args) throws NumberFormatException, IOException {
        // TODO Auto-generated method stub
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        StringBuilder sb = new StringBuilder();
 
        int T = Integer.parseInt(br.readLine().trim());
 
        // tc 수행
        for (int tc = 1; tc <= T; tc++) {
 
            // 입력 변수
            StringTokenizer st;
 
            // 자석을 회전시키는 횟수 입력
            int K = Integer.parseInt(br.readLine().trim());
 
            // 자성 정보 입력
            for (int i = 1; i <= 4; i++) {
                st = new StringTokenizer(br.readLine());
                for (int j = 0; j < 8; j++) {
                    magne[i][j] = Integer.parseInt(st.nextToken());
                }
            }
 
            // 자석의 회전 정보 입력
            for (int i = 0; i < K; i++) {
                // 자석의 번호와 방향 입력
                st = new StringTokenizer(br.readLine());
                int n = Integer.parseInt(st.nextToken());
                int d = Integer.parseInt(st.nextToken());
 
                // 1 ~ 4번 자석 방향 체크 초기화
                diretion = new int[5];
                 
                // 방향 체크
                process(n,d);
 
                // 회전 진행
                for (int j = 1; j <= 4; j++) {
                    if(diretion[j] != 0) 
                        rotate(j,diretion[j]);
                }
                 
            } // 입력 종료
 
            // 출력 내용 추가
            sb.append("#" + tc + " " + getScore() + "\n");
 
        } // tc 종료
 
        // 출력
        System.out.println(sb);
 
    } // 메인 종료
 
    // 이웃한 자석이 회전 가능한지 체크하는 함수
    static void process(int id, int dir) {
 
        // 회전 방향 저장
        diretion[id] = dir;
 
        // 왼쪽 탐색
        // 왼쪽에 회전할 자석이 존재하고, 회전 체크가 안될 경우 진행
        // id가 1이면 더 이상 탐색할 자석이 없기 때문
        // 회전 체크하는 이유는 재귀할 때 이미 체크한 곳을 다시 확인하는 것을 방지
        if (id > 1 && diretion[id - 1] == 0) {
 
            // 자신의 6번과 오른쪽의 2번이 붙어 있는 자석이다.
            // 붙어 있는 자석이 서로 다르면 반대 방향으로 1칸 회전한다.
            if (magne[id][6] != magne[id - 1][2]) {
                process(id - 1, -dir);
            }
 
        }
 
        // 오른쪽 탐색
        // 오른쪽에 회전할 자석이 존재하고, 회전 체크가 안될 경우 진행
        // id가 4이면 더 이상 탐색할 자석이 없기 때문
        // 회전 체크하는 이유는 재귀할 때 이미 체크한 곳을 다시 확인하는 것을 방지
        if (id < 4 && diretion[id + 1] == 0) {
 
            // 자신의 2번과 왼쪽 6번이 붙어 있는 자석이다.
            // 붙어 있는 자석이 서로 다르면 반대 방향으로 1칸 회전한다.
            if (magne[id][2] != magne[id + 1][6]) {
                process(id + 1, -dir);
            }
        }
 
    } // 체크 함수 종료
 
    // 회전하는 함수
    static void rotate(int id, int dir) {
        // 시계 방향 회전
        // 오른쪽 시프트 연산
        if(dir==1) {
            // 맨 뒤의 원소 추출
            int temp = magne[id][7];
             
            // 원소 오른쪽으로 이동
            for (int i = 7; i > 0; i--) {
                magne[id][i] = magne[id][i-1];
            }
             
            // 첫번째 원소 갱신
            magne[id][0] = temp;
             
        }
        // 반시계 회전
        // 왼쪽 시프트 연산
        else {
             
            // 맨 앞에 원소 추출
            int temp = magne[id][0];
             
            // 원소 왼쪽으로 이동
            for (int i = 0; i < 7; i++) {
                magne[id][i] = magne[id][i+1];
            }
             
            // 마지막 원소 갱신
            magne[id][7] = temp;
        }
         
 
    } // 회전 함수 종료
 
    // 점수를 계산하는 함수
    static int getScore() {
        int score = 0;
 
        if (magne[1][0] != 0)
            score += 1;
        if (magne[2][0] != 0)
            score += 2;
        if (magne[3][0] != 0)
            score += 4;
        if (magne[4][0] != 0)
            score += 8;
 
        return score;
 
    }
 
}

카테고리:

업데이트: