종이의 개수

조소복·2022년 9월 5일
0
post-custom-banner

문제

N×N크기의 행렬로 표현되는 종이가 있다. 종이의 각 칸에는 -1, 0, 1 중 하나가 저장되어 있다. 우리는 이 행렬을 다음과 같은 규칙에 따라 적절한 크기로 자르려고 한다.

  1. 만약 종이가 모두 같은 수로 되어 있다면 이 종이를 그대로 사용한다.
  2. (1)이 아닌 경우에는 종이를 같은 크기의 종이 9개로 자르고, 각각의 잘린 종이에 대해서 (1)의 과정을 반복한다.

이와 같이 종이를 잘랐을 때, -1로만 채워진 종이의 개수, 0으로만 채워진 종이의 개수, 1로만 채워진 종이의 개수를 구해내는 프로그램을 작성하시오.

입력

첫째 줄에 N(1 ≤ N ≤ 37, N은 3k 꼴)이 주어진다. 다음 N개의 줄에는 N개의 정수로 행렬이 주어진다.

출력

첫째 줄에 -1로만 채워진 종이의 개수를, 둘째 줄에 0으로만 채워진 종이의 개수를, 셋째 줄에 1로만 채워진 종이의 개수를 출력한다.

예제 입력 1

9
0 0 0 1 1 1 -1 -1 -1
0 0 0 1 1 1 -1 -1 -1
0 0 0 1 1 1 -1 -1 -1
1 1 1 0 0 0 0 0 0
1 1 1 0 0 0 0 0 0
1 1 1 0 0 0 0 0 0
0 1 -1 0 1 -1 0 1 -1
0 -1 1 0 1 -1 0 1 -1
0 1 -1 1 0 -1 0 1 -1

예제 출력 1

10
12
11

문제 풀이

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

public class BJ1780 {
    static int nag;
    static int zero;
    static int pos;
    static int[][] paper;

    public static void main(String[] args) throws IOException {
        BufferedReader br=new BufferedReader(new InputStreamReader(System.in));

        int N=Integer.parseInt(br.readLine());

        paper=new int[N][N];

        for(int i=0;i<N;i++){
            StringTokenizer st=new StringTokenizer(br.readLine());
            for(int j=0;j<N;j++){
                paper[i][j]=Integer.parseInt(st.nextToken());
            }
        }

        dfs(N,0,0);

        System.out.println(nag+" "+zero+" "+pos);

    }

    //9개의 크기로 자르고 재귀를 통해 모두 같은 숫자인지 확인
    public static void dfs(int N,int startI,int startJ){
        int tmp=paper[startI][startJ];
        boolean flag=false;

        //모두 같은 숫자인지 확인
        for(int i=startI;i<startI+N;i++){
            for(int j=startJ;j<startJ+N;j++){
                if(tmp!=paper[i][j]){
                    flag=true;
                    break;
                }
            }
            if(flag) break;
        }

        if(!flag){
            if(tmp==-1) nag++;
            else if(tmp==0) zero++;
            else if(tmp==1) pos++;
        }else{
            for(int i=startI;i<startI+N;i+=N/3){
                for(int j=startJ;j<startJ+N;j+=N/3){
                    dfs(N/3,i,j);
                }
            }
        }
    }
}

문제의 요구사항은 간단하다.

  • 배열에 든 숫자들이 모두 같은 숫자인지 확인
  • 모두 같은 숫자가 아니라면 9개로 나눠서 반복확인

이때 9개로 나눈뒤 모두 같은 숫자인지 확인하기 위해 재귀방식을 이용해야한다는 것을 생각해 볼 수 있다.


해당하는 크기의 숫자들이 모두 같은지 확인

int tmp=paper[startI][startJ];
boolean flag=false;

//모두 같은 숫자인지 확인
for(int i=startI;i<startI+N;i++){
      for(int j=startJ;j<startJ+N;j++){
           if(tmp!=paper[i][j]){
                flag=true;
                break;
           }
      }
      if(flag) break;
}

재귀로 넘겨줄 때, 종이의 크기와 paper 배열을 기준으로 봤을 때, i와 j의 시작하는 위치 (startI, startJ) 를 넘겨준다.

그렇다면 for문을 돌려 확인할 때도 [startI, startJ]부터 [startI+N, startJ+N]의 좌표까지만 확인하면 된다.

제일 앞의 숫자를 tmp라는 변수에 담아두고 그 값과 같은지 확인해본다. 다른 숫자가 나온다면 flagtrue로 바꿔주고 for문을 끝내서 9개 로 나누어 재귀를 돌릴 준비를 한다.


종이를 9개로 나누고 재귀 돌리기

if(!flag){
       if(tmp==-1) nag++;
       else if(tmp==0) zero++;
       else if(tmp==1) pos++;
}else{
       for(int i=startI;i<startI+N;i+=N/3){
            for(int j=startJ;j<startJ+N;j+=N/3){
                dfs(N/3,i,j);
            }
       }
}

flagfalse라면 모두 같은 숫자라는 뜻이기 때문에 -1,0,1인지 확인한 후 해당하는 숫자의 카운트를 올려준다.

flagtrue라면 모두 같은 숫자가 아니라는 뜻이기 때문에 현재의 종이 크기와 위치에서 행과 열을 3으로 나눠줘야한다.

우선 N의 크기는 3으로 나눠주고 어느 위치에서 시작할지 판단하기 위해서는 현재의 크기에서 3으로 나눈 크기만큼의 위치를 dfs의 startI, startJ 변수 값으로 보내준다.

아래의 사진을 참고하면 쉽게 이해할 수 있을 것 같다.


이런 로직을 이용하면 금방 구현할 수 있다!



소소하게 알게된 팁..!
답을 출력할 때 무조건 출력형식에 맞게 다음 줄에 출력해야하는 줄 알았는데 공백을 주고 출력해도 정답 처리가 됐다!

profile
개발을 꾸준히 해보자
post-custom-banner

0개의 댓글