[JAVA] 백준 5904번: Moo 게임
백준 5904번
https://www.acmicpc.net/problem/5904
문제
문제는 이해하기 쉽다.
moo 들의 조합으로 이루어진 문자열의 규칙을 찾아서,
입력되는 N번째의 문자가 어떤 문자('m' 또는 'o')인지 출력하면 된다.
보면 규칙적으로 m은 단어의 첫 번째에서만 나온다.
규칙 찾고, 첫 번째 단어인지 체크 → 맞으면 'm', 아니면 'o'를 출력하면 끝
이제 규칙을 찾아보자
규칙
문자열이 길어지는 규칙을 보면
S[k] = S[k-1] + moo+o(k) + S[k-1]* 이다.
즉, 가운데를 기준으로 앞, 뒤에 이전 값이 붙는다.
이를 토대로
입력된 숫자(N)가 10 정도 된다고 가정했을 경우,
수많은 moo 들을 크게
왼쪽 moo(Left) / 가운데 moo(Middle) / 오른쪽 moo(Right) 들로 나누어 보았다.
예시 > S(1) = "m o o / m o o o / m o o"
왼쪽 moo(Left) / 가운데 moo(Middle) / 오른쪽 moo(Right)
위와 같이 나누어 생각한 후 입력된 N의 범위가
어디에 해당하는지 판별한다.
그리고 해당하는 범위로 재귀를 호출해준다.
(가장 작은 moo는 'moo' 이기 때문에 moo까지 쪼개졌으면 종료)
👆🏻N의 범위 : 1 ≤ N ≤ 10^9
(N의 크기를 보면, 이분 탐색을 통해 크기를 절반씩 줄여나가며 탐색해야 시간 복잡도를 줄일 수 있다)
💡아이디어 : 이분 탐색
(이분 탐색은 보통 재귀 함수로 구현)
(장점: 구현과 가독성이 좋다)
✍🏻점화식
S[k+1] = S[k] + (4+k) + S[k];
(4+k) 는?
예시 > S(1) = "m o o / m o o o / m o o"
(1(m의 개수) + 2(o의 기본 개수) + k+1) ⇒ k+4
재귀 함수 구조
private static char moo(범위){
//기저조건
if(범위가 3이하인 경우){
return 'm' or 'o';
}else{
// N 이 왼쪽 영역인 경우
return moo(Left);
// N 이 오른쪽 영역인 경우
return moo(Right);
// N 이 가운데 영역인 경우
return 'm' or 'o';
}
}
[JAVA] 해설 코드
package week06;
import java.util.Scanner;
public class BOJ_5904 {
static int n;
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
n = sc.nextInt();
// S[0]일 때, 문자열의 총 길이
int num = 3;
// S[k-1]의 마지막 index
int preK = 3;
// index
int k = 0;
// n의 범위를 구해주기
while(num<n) {
k++;
num = 2 * preK + (1+2+k);
preK = num;
}
// 재귀함수에 시작값으로 넣어주기 위해 preK 을 다시 구함
preK = (num-(1+2+k))/2;
char c=' ';
// 입력된 숫자가 3 이하일 경우
if(k==0) {
if(n==1)c = 'm';
else c = 'o';
}else {
// 3보다 클 경우
if(preK+1<=n && n<preK+(1+2+k)) {
if(preK+1==n) {
c = 'm';
}else c = 'o';
}else
c = moo(preK+1+(1+2+k),k-1,num);
}
System.out.println(c);
}
// 시작 index, k 값, 끝 index
private static char moo(int si, int k, int se) {
// 중간 moo의 크기 구하기
int index = ((se-si+1 -(k + 3))/2);
// 기저조건
if(k==0) {
if(n==si) return 'm';
else return 'o';
}else {
//왼쪽인 경우
if(si<=n && n<si+index) {
if(si==n) return 'm';
return moo(si, k-1, si+index-1);
//else return 'o';
}
//오른쪽인 경우
else if(si+index+(k + 3)<=n && n<=se) {
if(si+index+(k + 3)==n) return 'm';
else return moo(si+index+(k + 3), k-1, se);
}
//가운데
else{
if(si+index==n) return 'm';
else return 'o';
}
}
}
}
메모리와 시작은 이 정도 나왔다.
추후에 이 문제를 더 보기 쉽고 간결하게 다시 짜 볼 생각이다.
'알고리즘 풀이' 카테고리의 다른 글
[JAVA]백준 1010번: 다리 놓기 (0) | 2021.09.09 |
---|---|
[JAVA]프로그래머스_N으로 표현 (level 3) (0) | 2021.09.09 |