(2116) 주사위 쌓기

🌍TISTORY

문제 🌐

천수는 여러 종류의 주사위를 가지고 쌓기 놀이를 하고 있다. 주사위의 모양은 모두 크기가 같은 정육면체이며 각 면에는 1부터 6까지의 숫자가 하나씩 적혀 있다. 그러나 보통 주사위처럼 마주보는 면에 적혀진 숫자의 합이 반드시 7이 되는 것은 아니다.

주사위 쌓기 놀이는 아래에서부터 1번 주사위, 2번 주사위, 3번 주사위, … 의 순서로 쌓는 것이다. 쌓을 때 다음과 같은 규칙을 지켜야 한다: 서로 붙어 있는 두 개의 주사위에서 아래에 있는 주사위의 윗면에 적혀있는 숫자는 위에 있는 주사위의 아랫면에 적혀있는 숫자와 같아야 한다. 다시 말해서, 1번 주사위 윗면의 숫자는 2번 주사위 아랫면의 숫자와 같고, 2번 주사위 윗면의 숫자는 3번 주사위 아랫면의 숫자와 같아야 한다. 단, 1번 주사위는 마음대로 놓을 수 있다.

이렇게 쌓아 놓으면 긴 사각 기둥이 된다. 이 사각 기둥에는 4개의 긴 옆면이 있다. 이 4개의 옆면 중에서 어느 한 면의 숫자의 합이 최대가 되도록 주사위를 쌓고자 한다. 이렇게 하기 위하여 각 주사위를 위 아래를 고정한 채 옆으로 90도, 180도, 또는 270도 돌릴 수 있다. 한 옆면의 숫자의 합의 최댓값을 구하는 프로그램을 작성하시오.

입력

첫줄에는 주사위의 개수가 입력된다. 그 다음 줄부터는 한 줄에 하나씩 주사위의 종류가 1번 주사위부터 주사위 번호 순서대로 입력된다. 주사위의 종류는 각 면에 적혀진 숫자가 그림1에 있는 주사위의 전개도에서 A, B, C, D, E, F 의 순서로 입력된다. 입력되는 숫자 사이에는 빈 칸이 하나씩 있다. 주사위의 개수는 10,000개 이하이며 종류가 같은 주사위도 있을 수 있다.

출력

첫줄에 한 옆면의 숫자의 합이 가장 큰 값을 출력한다.

https://upload.acmicpc.net/64d6b360-8f57-4764-a5a7-28a39cd86a8a/-/preview/

예제 입력 1

5
2 3 1 6 5 4
3 1 2 4 6 5
5 6 4 1 3 2
1 3 6 2 4 5
4 1 6 5 2 3

예제 출력 1

29

접근 방법

보통의 주사위처럼 한 면의 숫자가 정해졌을 때, 반대쪽 면의 숫자가 자동으로 정해진다면 답은 6*N으로 나와 있는 문제였다.

하지만 이 문제에서 주사위는 보통의 주사위가 아니기 때문에 하나씩 생각해야 했다.

주사위와 그 면을 어떻게 표현할 것인가? 반대쪽 면은 어떻게 접근할 것인가?

각각의 주사위를 일차원 배열로 관리할 수도 있었으나, 주사위의 개수가 몇 개가 들어올지 모르기 때문에 N칸짜리 배열에 한 번 더 넣어야 했다. 그래서 N*6 크기의 이차원 배열을 생각했다.

[A, B, C, D, E, F] 형태로 주사위를 관리했을 때 마주보는 면은 2만큼 차이가 나기 때문에 인덱스로 관리하기 편할 거라고 생각했는데… A, F의 경우 인덱스로 접근하려면 예외 처리를 해 줘야 했다.

N 개의 주사위와 6면을 관리하기 위해 첫 번째로 이차원 배열을 생각했다.

인덱스 접근이 번거롭다면? Dice class와 메소드를 만들자

차라리 Dice라는 클래스와 메소드를 만들어 관리하는 것이 편할 것이라 생각했다.

클래스 정의와 생성자

class Dice {
    int a;
    int b;
    int c;
    int d;
    int e;
    int f;
    public Dice(int a, int b, int c, int d, int e, int f) {
        super();
        this.a=a;
        this.b=b;
        this.c=c;
        this.d=d;
        this.e=e;
        this.f=f;
    }

메소드 구현

옆면의 합 중 최대값을 구해야 하기 때문에 윗면과 아랫면이 정해지면 주사위를 돌리면 최대값을 구할 수 있다. 따라서 나머지 4개 면 중에서 가장 큰 값만 리턴 하면 된다.

opposite 메소드: 1~6 사이의 숫자를 받았을 때 반대쪽 면에 있는 숫자를 리턴max 메소드: 위/아래 값을 줬을 때 옆면 중 최대값을 리턴

    public int opposite(int x) {
        if (a==x) return f;
        else if (b==x) return d;
        else if (c==x) return e;
        else if (d==x) return b;
        else if (e==x) return c;
        else if (f==x) return a;
        return -1;
    }

max 메소드: 위/아래 값을 줬을 때 옆면 중 최대값을 리턴

    public int max(int x) {
        if (a==x || f==x) {
            return Math.max(Math.max(b, d), Math.max(c,  e));
        }

        else if (b==x || d==x) {
            return Math.max(Math.max(a, f), Math.max(c, e));
        }

        else if (c==x || e==x) {
            return Math.max(Math.max(a, f), Math.max(b, d));
        }
        return -1;
    }

이 메소드를 이용하여 반복문을 구현했다.

첫 번째 숫자의 윗면이 정해진다면 나머지 N-1개의 주사위의 윗면, 아랫면도 정해지기 때문에 이를 바탕으로 옆면 4개 중 최대값을 찾아줬다.

N-1 만큼 while문을 돌면서 이전 값을 바탕으로 top, bottom의 숫자를 갱신하고 최대값을 찾아 더했다.

첫 번째 주사위의 윗면이 1일 때부터 6일 때까지 숫자들의 합을 구하고 최대값을 갱신한다.

        int answer = 0;
        for (int i=1; i<=6; i++) {
            int temp = 0;
            int top = i;
            int bottom = dices[0].opposite(i);
            temp += dices[0].max(top);
            int idx = 1;
            while (idx<N) {
                top = bottom;
                bottom = dices[idx].opposite(top);
                temp += dices[idx].max(top);
                idx++;
            }
            answer = Math.max(temp, answer);
        }
        System.out.println(answer);
    }

Full Solution

import java.util.Scanner;
class Dice {
    int a;
    int b;
    int c;
    int d;
    int e;
    int f;

    public Dice(int a, int b, int c, int d, int e, int f) {
        super();
        this.a=a;
        this.b=b;
        this.c=c;
        this.d=d;
        this.e=e;
        this.f=f;
    }

    public int opposite(int x) {
        if (a==x) return f;
        else if (b==x) return d;
        else if (c==x) return e;
        else if (d==x) return b;
        else if (e==x) return c;
        else if (f==x) return a;
        return -1;
    }

    public int max(int x) {
        if (a==x || f==x) {
            return Math.max(Math.max(b, d), Math.max(c,  e));
        }

        else if (b==x || d==x) {
            return Math.max(Math.max(a, f), Math.max(c, e));
        }

        else if (c==x || e==x) {
            return Math.max(Math.max(a, f), Math.max(b, d));
        }
        return -1;
    }
}
public class Main {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int N = sc.nextInt();
        // 전체 주사위 담는 배열
        Dice[] dices = new Dice[N];

        for (int i=0; i<N; i++) {
            int a = sc.nextInt();
            int b = sc.nextInt();
            int c = sc.nextInt();
            int d = sc.nextInt();
            int e = sc.nextInt();
            int f = sc.nextInt();
            dices[i] = new Dice(a, b, c, d, e, f);
        }

        int answer = 0;
        for (int i=1; i<=6; i++) {
            int temp = 0;
            int top = i;
            int bottom = dices[0].opposite(i);
            temp += dices[0].max(top);
            int idx = 1;
            while (idx<N) {
                top = bottom;
                bottom = dices[idx].opposite(top);
                temp += dices[idx].max(top);
                idx++;
            }
            answer = Math.max(temp, answer);
        }
        System.out.println(answer);
    }
}