본문 바로가기
프로그래밍/백준 복기

[구현] 백준 20061: 모노미노도미노 2

by 개발도사(진) 2022. 7. 27.

이번 문제도 엄청 길어서 캡처 대신 링크로 갈음한다.

20061번: 모노미노도미노 2 (acmicpc.net)

 

20061번: 모노미노도미노 2

모노미노도미노는 아래와 같이 생긴 보드에서 진행되는 게임이다. 보드는 빨간색 보드, 파란색 보드, 초록색 보드가 그림과 같이 붙어있는 형태이다. 게임에서 사용하는 좌표 (x, y)에서 x는 행,

www.acmicpc.net

 

어제 새벽 서너시까지 잡고 풀었는데, 문제가 딱히 되짚을 게 없다... 그냥 빡구현이다.

 

채워넣고, 조건 맞으면 점수 올리고, 연하게 색칠된 특별 구역에 들어가면 그만큼 내려주고... 만 반복하면 된다. 

 

어차피 빨강 배열은 볼 필요가 없다. 초록 배열, 파랑 배열만 가지고 작업하면 된다.

 

어떤 블록을 넣어줄지 지정해주는 t, 배열이 들어갈 인덱스를 지정해주는 x,y를 받으면, switch문을 이용해 알맞은 블록을 밀어넣기로 했다. 1칸 이상의 블록은 해당 블록이 시작하는 지점 뿐 아니라 블록 크기에 해당하는 다른 지점에서도 그 다음 칸에 블록이 있는지 판단해 줘야 한다는 점만 기억하면 된다.

    //넣는 함수
    static void pushBlocks(int t, int x, int y){
        boolean isGreenChanged=false;
        boolean isBlueChanged=false;

        switch(t){
            case 1:
                //1*1 블럭
                for(int i=0;i< greenBoard.length-1;i++){
                    if(greenBoard[i+1][y]==1){
                        //이미 채워진 블록을 만나면, 그 위에 채워 넣으면 됨.
                        greenBoard[i][y]=1;
                        isGreenChanged=true;
                        break;
                    }
                }
                //만일 끝까지 순회해 봤을 때, 1이 없다면 그 칸에 넣어 줘야 함.
                if(!isGreenChanged){
                    greenBoard[greenBoard.length-1][y]=1;
                }

                for(int i=0;i<5;i++){
                    if(blueBoard[x][i+1]==1){
                        blueBoard[x][i]=1;
                        isBlueChanged=true;
                        break;
                    }
                }
                if(!isBlueChanged){
                    blueBoard[x][5]=1;
                }
                //뭐가 들어간들 일단 두 칸은 색칠됨.
                break;
            case 2:
                //1*2블럭
                for(int i=0;i< greenBoard.length-1;i++){
                    if(greenBoard[i+1][y]==1||greenBoard[i+1][y+1]==1){
                        greenBoard[i][y]=1;
                        greenBoard[i][y+1]=1;

                        isGreenChanged=true;
                        break;
                    }
                }
                if(!isGreenChanged){
                    greenBoard[5][y]=1;
                    greenBoard[5][y+1]=1;

                }
                //파랑_가로로 날아가는 점 고려해서, index의 두 칸 앞으로 판단해야 함.
                for(int i=0;i<4;i++){
                    if(blueBoard[x][i+2]==1){
                        blueBoard[x][i]=1;
                        blueBoard[x][i+1]=1;
                        isBlueChanged=true;
                        break;
                    }
                }
                if(!isBlueChanged){
                    blueBoard[x][4]=1;
                    blueBoard[x][5]=1;
                    break;
                }
                break;
            case 3:
                //2*1블럭
                for(int i=0;i< greenBoard.length-2;i++){
                    if(greenBoard[i+2][y]==1){
                        greenBoard[i][y]=1;
                        greenBoard[i+1][y]=1;
                        isGreenChanged=true;
                        break;
                    }
                }
                if(!isGreenChanged){
                    greenBoard[4][y]=1;
                    greenBoard[5][y]=1;
                }

                for(int i=0;i<5;i++){
                    if(blueBoard[x][i+1]==1||blueBoard[x+1][i+1]==1){
                        blueBoard[x][i]=1;
                        blueBoard[x+1][i]=1;

                        isBlueChanged=true;
                        break;
                    }
                }
                if(!isBlueChanged){
                    blueBoard[x][5]=1;
                    blueBoard[x+1][5]=1;
                }
                break;
        }
    }

블록을 채워 넣으면서 굳이 블록 갯수를 더하지 않았다. 어차피 블록 갯수는 다 계산하고 난 후에 출력 직전 딱 한 번만 셀 것이다. 

 

내려줄 때는 큐를 이용해서 헷갈리지 않게 구현했다.

    //지우는 함수
    static void checkAndDelete(){
        //초록
        int greenIdxToChange = -1;
        for(int i=0;i< greenBoard.length;i++){
            for(int j=0;j< greenBoard[i].length;j++){
                if(greenBoard[i][j]==0){
                    //하나라도 false가 나오면 탈출해서 다음 줄로 넘어가야 함.
                    greenIdxToChange=-1;
                    break;
                }else if(greenBoard[i][j]==1){
                    greenIdxToChange=i;
                }
            }
            //한 row를 다 돌았을 때, 그 줄이 채워져 있으면 줄을 지워줘야 함.
            if(greenIdxToChange!=-1){
                Queue<Integer> queue = new LinkedList<>();
                for(int j=0;j<greenIdxToChange;j++){
                    for(int k=0;k<greenBoard[j].length;k++){
                        queue.offer(greenBoard[j][k]);
                    }
                }
                for(int j=1;j<=greenIdxToChange;j++){
                    for(int k=0;k<greenBoard[j].length;k++){
                        greenBoard[j][k]=queue.poll();
                    }
                }
                for(int k=0;k<greenBoard[0].length;k++){
                    greenBoard[0][k]=0;
                }
               //줄 지우면, 채워진 칸의 수는 4개씩 빠지고, 점수 1점 상승
               score+=1;
            }
        }

        //파랑
        int blueIdxToChange = -1;
        for(int i=0;i<6;i++){
            for(int j=0;j<4;j++){
                if(blueBoard[j][i]==0){
                    //하나라도 false가 나오면 탈출해서 다음 줄로 넘어가야 함.
                    blueIdxToChange=-1;
                    break;
                }else if(blueBoard[j][i]==1){
                    blueIdxToChange=i;
                }
            }
            //한 row를 다 돌았을 때, 그 줄이 채워져 있으면 줄을 지워줘야 함.
            if(blueIdxToChange!=-1){
                Queue<Integer> queue = new LinkedList<>();
                for(int j=0;j<blueIdxToChange;j++){
                    for(int k=0;k<4;k++){
                        queue.offer(blueBoard[k][j]);
                    }
                }
                for(int j=1;j<=blueIdxToChange;j++){
                    for(int k=0;k<4;k++){
                        blueBoard[k][j]=queue.poll();
                    }
                }
                for(int k=0;k<blueBoard.length;k++){
                    blueBoard[k][0]=0;
                }
                score+=1;
            }
        }
    }

둘러보면서 만일 지워야 할 줄이 있다면, 어떤 줄을 지워야 할지 index를 설정해 주고, 큐를 사용해 한 줄씩 내려 주고 점수를 올리면 된다.

 

지워야할 줄을 지워준 다음에는 이제 특별구역(연한 구역)에 남아 있는 칸이 있는지 확인하고, 있다면 그 칸이 연한 구역 아래로 내려가도록 블록들을 밀어 주어야 한다. 

 

(Color)Push라는 변수를 선언하고, 특별 구역을 순회하면서 만일 색칠된 영역이 있다면 (Color)Push가 그에 따라 밀어버려야 할 칸 개수를 지시하도록 했다. 이후에는 큐를 이용해 칸들을 밀어 주었다.

 

    //special region_연한 구역을 다루는 함수
    static void specialRegion(){
        int greenPush=0;
        int bluePush=0;
        for(int i=0;i<4;i++){
            if(greenBoard[0][i]==1){
                greenPush=2;
                break;
            }
            if(greenBoard[1][i]==1){
                greenPush=1;
            }
        }
        if(greenPush!=0){
            Queue<Integer> queue=new LinkedList<>();
            for(int j=2-greenPush;j<greenBoard.length-greenPush;j++){
                for(int k=0;k<greenBoard[j].length;k++){
                    queue.offer(greenBoard[j][k]);
                }
            }
            for(int j=2;j<greenBoard.length;j++){
                for(int k=0;k<greenBoard[j].length;k++){
                    greenBoard[j][k]=queue.poll();
                }
            }
            for(int i=0;i<2;i++){
                for(int j=0;j<4;j++){
                    greenBoard[i][j]=0;
                }
            }
        }

        //파랑
        for(int i=0;i<4;i++){
            if(blueBoard[i][0]==1){
                bluePush=2;
                break;
            }
            if(blueBoard[i][1]==1){
                bluePush=1;
                break;
            }
        }
        if(bluePush!=0){
            Queue<Integer> queue=new LinkedList<>();
            for(int j=2-bluePush;j<6-bluePush;j++){
                for(int k=0;k<4;k++){
                    queue.offer(blueBoard[k][j]);
                }
            }
            for(int j=2;j<6;j++){
                for(int k=0;k<4;k++){
                    blueBoard[k][j]=queue.poll();
                }
            }
            //0으로 맞춰주기
            for(int i=0;i<4;i++){
                for(int j=0;j<2;j++){
                    blueBoard[i][j]=0;
                }
            }
        }
    }

 

사실 그냥 딱히 생각할 것이 없었고 구현하면 되는 것이었다.

 

그런데 거의 밤새 잡고 있었다. 구현 문제가 야속한 게 나는 알 것 같은데 해 보면 안 된다. 어디서 나는 한참을 해도 안 되어서 그냥 잠깐 누웠다가 천천히 다시 뜯어봤는데, 반복문 인덱스 단 하나!!!!를 잘못 써서 그 사달이 난 것이었다. 

 

구현 문제들을 풀면 풀수록 이런 것들을 안 흘리는 게 진짜 실력인 것 같다. 그리고 그런 실수들을 빨리 캐치해 내려면, 한 줄 한 줄 칠 때 무작정 두들기지 말고 계속 생각해 보고, 또 막혔을 때 자꾸 편하게 고치려고 이것저것 쿡쿡 고쳐보지 말고 침착하게 뜯어봐야 하는 것 같다. 빡구현들은 답안이 몇백줄을 넘어가는 경우가 있어서 막막하기는 한데, 그렇게 돌아가는 게 오히려 빨리 가는 길인 듯 하다.

 

또, 아직까지도 col->row 순서로 반복하는 경우에 자주 헷갈리는 것 같다. 이 문제 같은 경우는 애초에 크기가 고정되어 있어서, 그냥 정수로 할 것을 length 이용해서 평소처럼 하다가 고생을 좀 더 한 것 같다.

 

코드 첨부하고 마친다.

import java.io.*;
import java.util.LinkedList;
import java.util.Queue;

public class BOJ_20061 {
    static int score=0;
    static int numBlocks=0;
    static int[][] greenBoard = new int[6][4];
    static int[][] blueBoard = new int[4][6];
    public static void main(String[] args) throws IOException{
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        int N = Integer.parseInt(br.readLine());
        for(int i=0;i<N;i++){
            String[] txy = br.readLine().split(" ");
            int t = Integer.parseInt(txy[0]);
            int x = Integer.parseInt(txy[1]);
            int y = Integer.parseInt(txy[2]);

            pushBlocks(t,x,y);
            checkAndDelete();
            specialRegion();

        }
        System.out.println(score);
        for(int i=0;i<6;i++){
            for(int j=0;j<4;j++){
                if(greenBoard[i][j]==1){
                    numBlocks++;
                }
                if(blueBoard[j][i]==1){
                    numBlocks++;
                }
            }
        }
        System.out.println(numBlocks);
    }

    //넣는 함수
    static void pushBlocks(int t, int x, int y){
        boolean isGreenChanged=false;
        boolean isBlueChanged=false;

        switch(t){
            case 1:
                //1*1 블럭
                for(int i=0;i< greenBoard.length-1;i++){
                    if(greenBoard[i+1][y]==1){
                        //이미 채워진 블록을 만나면, 그 위에 채워 넣으면 됨.
                        greenBoard[i][y]=1;
                        isGreenChanged=true;
                        break;
                    }
                }
                //만일 끝까지 순회해 봤을 때, 1이 없다면 그 칸에 넣어 줘야 함.
                if(!isGreenChanged){
                    greenBoard[greenBoard.length-1][y]=1;
                }

                for(int i=0;i<5;i++){
                    if(blueBoard[x][i+1]==1){
                        blueBoard[x][i]=1;
                        isBlueChanged=true;
                        break;
                    }
                }
                if(!isBlueChanged){
                    blueBoard[x][5]=1;
                }
                //뭐가 들어간들 일단 두 칸은 색칠됨.
                break;
            case 2:
                //1*2블럭
                for(int i=0;i< greenBoard.length-1;i++){
                    if(greenBoard[i+1][y]==1||greenBoard[i+1][y+1]==1){
                        greenBoard[i][y]=1;
                        greenBoard[i][y+1]=1;

                        isGreenChanged=true;
                        break;
                    }
                }
                if(!isGreenChanged){
                    greenBoard[5][y]=1;
                    greenBoard[5][y+1]=1;

                }
                //파랑_가로로 날아가는 점 고려해서, index의 두 칸 앞으로 판단해야 함.
                for(int i=0;i<4;i++){
                    if(blueBoard[x][i+2]==1){
                        blueBoard[x][i]=1;
                        blueBoard[x][i+1]=1;
                        isBlueChanged=true;
                        break;
                    }
                }
                if(!isBlueChanged){
                    blueBoard[x][4]=1;
                    blueBoard[x][5]=1;
                    break;
                }
                break;
            case 3:
                //2*1블럭
                for(int i=0;i< greenBoard.length-2;i++){
                    if(greenBoard[i+2][y]==1){
                        greenBoard[i][y]=1;
                        greenBoard[i+1][y]=1;
                        isGreenChanged=true;
                        break;
                    }
                }
                if(!isGreenChanged){
                    greenBoard[4][y]=1;
                    greenBoard[5][y]=1;
                }

                for(int i=0;i<5;i++){
                    if(blueBoard[x][i+1]==1||blueBoard[x+1][i+1]==1){
                        blueBoard[x][i]=1;
                        blueBoard[x+1][i]=1;

                        isBlueChanged=true;
                        break;
                    }
                }
                if(!isBlueChanged){
                    blueBoard[x][5]=1;
                    blueBoard[x+1][5]=1;
                }
                break;
        }
    }
    //지우는 함수
    static void checkAndDelete(){
        //초록
        int greenIdxToChange = -1;
        for(int i=0;i< greenBoard.length;i++){
            for(int j=0;j< greenBoard[i].length;j++){
                if(greenBoard[i][j]==0){
                    //하나라도 false가 나오면 탈출해서 다음 줄로 넘어가야 함.
                    greenIdxToChange=-1;
                    break;
                }else if(greenBoard[i][j]==1){
                    greenIdxToChange=i;
                }
            }
            //한 row를 다 돌았을 때, 그 줄이 채워져 있으면 줄을 지워줘야 함.
            if(greenIdxToChange!=-1){
                Queue<Integer> queue = new LinkedList<>();
                for(int j=0;j<greenIdxToChange;j++){
                    for(int k=0;k<greenBoard[j].length;k++){
                        queue.offer(greenBoard[j][k]);
                    }
                }
                for(int j=1;j<=greenIdxToChange;j++){
                    for(int k=0;k<greenBoard[j].length;k++){
                        greenBoard[j][k]=queue.poll();
                    }
                }
                for(int k=0;k<greenBoard[0].length;k++){
                    greenBoard[0][k]=0;
                }
               //줄 지우면, 채워진 칸의 수는 4개씩 빠지고, 점수 1점 상승
               score+=1;
            }
        }

        //파랑
        int blueIdxToChange = -1;
        for(int i=0;i<6;i++){
            for(int j=0;j<4;j++){
                if(blueBoard[j][i]==0){
                    //하나라도 false가 나오면 탈출해서 다음 줄로 넘어가야 함.
                    blueIdxToChange=-1;
                    break;
                }else if(blueBoard[j][i]==1){
                    blueIdxToChange=i;
                }
            }
            //한 row를 다 돌았을 때, 그 줄이 채워져 있으면 줄을 지워줘야 함.
            if(blueIdxToChange!=-1){
                Queue<Integer> queue = new LinkedList<>();
                for(int j=0;j<blueIdxToChange;j++){
                    for(int k=0;k<4;k++){
                        queue.offer(blueBoard[k][j]);
                    }
                }
                for(int j=1;j<=blueIdxToChange;j++){
                    for(int k=0;k<4;k++){
                        blueBoard[k][j]=queue.poll();
                    }
                }
                for(int k=0;k<blueBoard.length;k++){
                    blueBoard[k][0]=0;
                }
                score+=1;
            }
        }
    }
    //special region_연한 구역을 다루는 함수
    static void specialRegion(){
        int greenPush=0;
        int bluePush=0;
        for(int i=0;i<4;i++){
            if(greenBoard[0][i]==1){
                greenPush=2;
                break;
            }
            if(greenBoard[1][i]==1){
                greenPush=1;
            }
        }
        if(greenPush!=0){
            Queue<Integer> queue=new LinkedList<>();
            for(int j=2-greenPush;j<greenBoard.length-greenPush;j++){
                for(int k=0;k<greenBoard[j].length;k++){
                    queue.offer(greenBoard[j][k]);
                }
            }
            for(int j=2;j<greenBoard.length;j++){
                for(int k=0;k<greenBoard[j].length;k++){
                    greenBoard[j][k]=queue.poll();
                }
            }
            for(int i=0;i<2;i++){
                for(int j=0;j<4;j++){
                    greenBoard[i][j]=0;
                }
            }
        }

        //파랑
        for(int i=0;i<4;i++){
            if(blueBoard[i][0]==1){
                bluePush=2;
                break;
            }
            if(blueBoard[i][1]==1){
                bluePush=1;
                break;
            }
        }
        if(bluePush!=0){
            Queue<Integer> queue=new LinkedList<>();
            for(int j=2-bluePush;j<6-bluePush;j++){
                for(int k=0;k<4;k++){
                    queue.offer(blueBoard[k][j]);
                }
            }
            for(int j=2;j<6;j++){
                for(int k=0;k<4;k++){
                    blueBoard[k][j]=queue.poll();
                }
            }
            //0으로 맞춰주기
            for(int i=0;i<4;i++){
                for(int j=0;j<2;j++){
                    blueBoard[i][j]=0;
                }
            }
        }
    }

}