Study/Baekjoon

Baekjoon2447: 별 찍기 - 10

devyoseph 2021. 10. 15. 21:07

문제

재귀적인 패턴으로 별을 찍어 보자. N이 3의 거듭제곱(3, 9, 27, ...)이라고 할 때, 크기 N의 패턴은 N×N 정사각형 모양이다.

크기 3의 패턴은 가운데에 공백이 있고, 가운데를 제외한 모든 칸에 별이 하나씩 있는 패턴이다.

N이 3보다 클 경우, 크기 N의 패턴은 공백으로 채워진 가운데의 (N/3)×(N/3) 정사각형을 크기 N/3의 패턴으로 둘러싼 형태이다. 예를 들어 크기 27의 패턴은 예제 출력 1과 같다.

입력

첫째 줄에 N이 주어진다. N은 3의 거듭제곱이다. 즉 어떤 정수 k에 대해 N=3k이며, 이때 1 ≤ k < 8이다.

출력

첫째 줄부터 N번째 줄까지 별을 출력한다.

추상화

1) 모양 자체에서 규칙성을 발견한다
→ 한 줄 한 줄로는 규칙성을 찾을 수 없다

 

1항: *을 첫째줄 3번 출력 - 둘째줄 중앙 1만큼을 비운다 - 셋째줄 3번 출력

2항: 이전 패턴을 첫째줄 3번 출력 - 둘째줄 중앙 3만큼 비운다 - 셋째줄 3번 출력

3항


2) java 반복문을 위한 규칙성 정리
3항과 4항을 비교해보자

3항의 첫째줄을 3번 반복 = 4항의 첫째줄
3항의 둘째줄을 3번 반복 = 4항의 둘째줄
                            ...
3항의 아홉째줄을 3번 반복 = 4항의 아홉째줄


3항의 첫째줄을 1번 반복 + 3항의 길이만큼 띄어쓰기 + 첫째줄을 1회반복
3항의 둘째줄을 1번 반복 + 3항의 길이만큼 띄어쓰기 + 첫째줄을 1회반복
                           ...
3항의 아홉째줄을 1번 반복 + 3항의 길이만큼 띄어쓰기 + 아홉째줄을 1회반복

3) 코드화
i) 0항에 *을 넣고 반복문을 통해 문양을 저장한다
ii) 줄바꿈 "₩n"을 같이 저장할 수 있지만 같이 저장하면 다음 항에 값을 입력할 때 다시 줄바꿈의 위치를 바꿔야 하는 문제가 발생한다
         → 줄바꿈의 단계를 따로 만들어준다
         →즉, 배열 안 String 데이터들에는 줄바꿈이 없는 한 줄의 텍스트가 있는 것이다
iii) 텍스트 저장: 1항 = 3X3
         → 2항 첫째줄: 1항의 index[0] index[3의 1승 - 1]을 3번 반복
         → 2항 둘째줄: 1항의 index[3의 1승] index[3의 1승X2 - 1]을 3번 반복
         → 2항 셋째줄: 1항의 index[3의 1승X2] index[3의 1승X3 - 1]을 3번 반복
n항을 3구역으로 나누어서 정의해본다
1구역) n-1항의 index[0]~index[3의 n-1승 -1]을 3번 반복 + .... + n-1항의 index[3의 n-1승 X 2]~index[3의 n승-1]을 3번 반복
2구역) n-1항의 index[0]~index[3의 n-1승 -1]을 1번 반복+띄어쓰기+ n-항의 index[3의 n-1승 X 2]~index[3의 n승-1]을 1번 반복
3구역) = 1구역과 동일

4) 텍스트의 연산이 많으므로 StringBuilder을 활용한다

import java.io.*;
public class Main {
public static void main(String[] args) throws IOException{
	 BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
	 StringBuilder st = new StringBuilder();
	 StringBuilder blank = new StringBuilder();
	 String[] star = new String[8]; //0~7항까지 배열에 저장
	 star[0] = "*"; //시작을 위해 넣어준다
     
	 for(int i =1; i < 8; i++) {
		 int length = (int)Math.pow(3, i-1); //i항을 작성하기 위해 i-1항의 길이를 가져온다
		 for(int j = 0; j<length; j++) {
			 blank.append(" "); //공백도 미리 만들어준다
		 }
		 for(int j = 0; j<length; j++) { //첫째 구역
			 int start = j*length;
			 int last = (j+1)*length;
//이전 항의 문자값은 substring을 통해 가져온다: 0, 3을 적으면 index[0]~index[2]까지 가져온다
			 st.append(star[i-1].substring(start, last));
			 st.append(star[i-1].substring(start, last));
			 st.append(star[i-1].substring(start, last));
		 }
		 for(int j = 0; j<length; j++) { //둘째 구역
			 int start = j*length;
			 int last = (j+1)*length;
			 st.append(star[i-1].substring(start, last));
			 st.append(blank.toString());
			 st.append(star[i-1].substring(start, last));
		 }
		 for(int j = 0; j<length; j++) { //셋째 구역
			 int start = j*length;
			 int last = (j+1)*length;
			 st.append(star[i-1].substring(start, last));
			 st.append(star[i-1].substring(start, last));
			 st.append(star[i-1].substring(start, last));
		 }
		 star[i] = st.toString();
		 st.delete(0, st.length());
		 blank.delete(0, blank.length());
//매번 사용한 StringBuilder를 초기화한다: 공백 데이터 또한 초기화하는 것을 잊으면 안된다
	 }
	 for(int i =1; i < 8; i++) {
		 int length = (int)Math.pow(3, i);
		 int indexplus = 0;
		 st.append(star[i]);
		 for(int j = 1; j<length; j++) {
			 int now = j*length +indexplus;
			 st.insert(now, "\n");
			 indexplus++;
		 }
//작성해준 배열에서 줄바꿈을 추가한다.
//줄바꿈을 추가할 때마다 index의 개수가 증가하는 것을 고려한 변수 now를 생성한다
		 star[i] = st.toString();
		 st.delete(0, st.length());
//줄바꿈한 값을 넣어주고 초기화
	 }
     int N = Integer.parseInt(br.readLine());
     int number = 0;
     while(N!=1) {
    	 N /= 3;
    	 number++;
//입력값은 3의 제곱으로 주어지는데 반복문을 통해 입력값의 지수승을 구한다
     }
     System.out.print(star[number]);
}	 
}

 

 

개선: 이중배열의 사용
이중배열을 27X27크기로 만들었다고 가정한다: arr[27][27]


27X27은 9X9를 사용한 패턴이고 9X9는 3X3을 이용한 패턴이다

1) 주어진 입력값 N으로 빈 논리배열 [N][N]을 만든다
→ 공백 아니면 '*'을 사용하므로 boolean, 논리배열을 사용한다

2) 0항의 index[0][0]을 통해(*) 1항을 만들 수 있다 (*** * * ***)
     1항의 index[0][0]~index[2][2]를 통해 2항을 만들 수 있다

3) 이와 같은 규칙성을 활용해 배열을 채우고 배열의 내용과 줄바꿈을 이용해 출력한다
      N = 27 예시
      <1구역을 9구역에 모두 복사하기>
        → 배열에는 총 9개의 구역이 있다
        → 반복문을 통해 9개의 구역의 '시작점'에서 시작할 수 있다(for 2번사용)
        → '시작점'에서 index[0]~index[2]의 값을 복사한다(for 2번사용)
       ※ 결과: 9개의 구역에 1번 구역이 전부 복사되어있다
         
      <5구역 비우기>
      →N=27인 경우 index[3][3]부터 index[6][6]까지 false로 만들어준다

4) StringBuilder를 통해 true면 *, false면 " "추가하기

import java.io.*;
public class Main {
public static void main(String[] args) throws IOException{
	 BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
	 StringBuilder st = new StringBuilder();
	 int N = Integer.parseInt(br.readLine());
	 boolean[][] star = new boolean[N][N];
//입력받은 N을 통해 NXN 배열을 만든다
	 star[0][0] = true; // 0항 = "*"을 넣어준다
     
	 makeStar(N, star); //메소드를 만들어준다. 아래 메소드 정의 부분으로 간다
     
     //메소드를 통해 false, true표시한 내용을 출력하는 구간
	 for(int i=0; i<N; i++) {
		 for(int j=0; j<N; j++) {
			 if(star[i][j]==true) {
				 st.append("*");
			 } else {st.append(" ");}
		 }
		 st.append("\n");
	 }
	 System.out.println(st.toString());
}
public static void makeStar(int N, boolean[][] star) {
  int x = 3;
//초기값 x를 지정해준다
  while( N*3 != x) {
//while문의 loop 한 바퀴마다 x에 3이 곱해진다. x가 N보다 커지면 반복문이 멈춘다
	  int length = x/3;
//N = 27이라면 3X3의 배열을 9번 복사하기 때문에 27을 3으로 나눠서 기준 길이를 만든다

	for(int i = 0; i<length*3; i+=length) {
		  for(int j=0; j<length*3; j+=length) {
//위 두 개의 for문으로 각 구역의 시작 좌표를 설정할 수 있다. star[i][j]

			for(int k=0; k<length; k++) {
				  for(int m =0; m<length; m++) {
					  star[i+k][j+m] = star[k][m];
//각 구역의 시작점에서 1구역의 내용을 복사해줄 for문들이다
				  }
			  }
		  }
	  }
//9개의 구역에 모두 복사한 후 5구역을 비워준다
	  for(int k=length; k<length*2; k++) {
		 for(int m =length; m<length*2; m++) {
			star[k][m] = false;
				 }
			 }
	  x *= 3;
  }
 
}
}

*코드 길이와 계산시간이 단축되었다

'Study > Baekjoon' 카테고리의 다른 글

Baekjoon2798: 블랙잭  (0) 2021.10.17
Baekjoon11729*: 하노이 탑 이동 순서  (0) 2021.10.16
Baekjoon10870: 피보나치 수 5  (0) 2021.10.14
Baekjoon10872: 팩토리얼  (0) 2021.10.14
Baekjoon1002: 터렛, 낮은 정답률의 이유  (0) 2021.10.14