ALGORITHM/JUNGOL

[보충] 20210819 JUNGOL_1733 오목

lemon-scone 2021. 8. 19. 21:35

http://jungol.co.kr/bbs/board.php?bo_table=pbank&wr_id=1006&sca=99&sfl=wr_subject&stx=%EC%98%A4%EB%AA%A9 

 

JUNGOL

 

www.jungol.co.kr

 

package day0819;

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

public class JUNGOL_1733_sol {
	
	private static int[][] map; 
	
	public static void main(String[] args) throws IOException {
		
		BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
		StringBuilder sb = new StringBuilder();
		StringTokenizer st = null;
		
		// 원래 19이지만 2칸을 추가해준다는 의미를 전달 하기 위해  +2로 표현
		// 테두리를 둘렀을 때의 장단점 
		// 장점 : 가장자리 범위를 넘어가는 것에 대해서 조건 입력 x
		// 단점 : 메모리 낭비, (0,0)이 아니라 (1,1)부터 시작하는 거니까 인덱스에 대한 오류를 범할 수 o
		map = new int[19 + 2][19 + 2];
		
		// string 을 잘라서 배열에 저장하는 방법
		/*for (int i = 1; i <= 19; i++) {
			st = new StringTokenizer(br.readLine(), " ");
			for (int j = 0; j < map[i].length; j++) {
				map[i][j] = Integer.parseInt(st.nextToken());
			}
		}*/
		
		// split 나 StringTokenizer 로 자르지 않고 받는 방법 (시간 절약 차원)
		for (int i = 1; i <= 19; i++) {
			String line = br.readLine();
			for (int j = 1, index = 0; j < 19; j++, index=index+2) {
				// - '0' 을 하지 않아도 된다 (시간 절약 차원)
				map[i][j] = line.charAt(index) - '0';
			}
		}
		
		// 배열이 잘 만들어 졌는지 확인 (단계별 확인 필요)
		/*for (int i = 0; i < map.length; i++) {
			System.out.println(Arrays.toString(map[i]));
		}*/
		
		// 배열을 순회하면서 오목인지 판별
		for (int r = 1; r <= 19; r++) {
			for (int c = 1; c <= 19; c++) {
				// 모든 칸 순회하면서 r,c 에서 시작하는 오목인지를 탐색
				if (map[r][c] == 0) continue; // 돌이 놓여있지 않으면 다음칸으로 넘어가자
				int answerColor = complete(r,c); // 오목성공이면 돌색상을 리턴 (1:검 2:흰 0:오목아님)
				if(answerColor > 0) {
					System.out.println(answerColor);
					System.out.println(r + " " + c);
					return; // 프로그램 종료
				}
			}
		}
		System.out.println(0); // 오목 승부 나지 않음
	} // end of main

	
	private static int[] dr = {-1, 0, 1, 1}; // 우상, 우, 우하, 하
	private static int[] dc = {1, 1, 1, 0}; // 우상, 우, 우하, 하
	
	
	/** r,c 좌표에서 시작하는 오목인지 확인, 오목성공이면 오목색상을 리턴 (1:검 2:흰 0:오목아님) */
	private static int complete(int r, int c) {
		
		int color = map[r][c];
		for (int k = 0; k < dc.length; k++) { // 4가지 오목방향
			
			// 이전칸이 나랑 다른 색인지 확인
			if (map[r-dr[k]][c-dc[k]] == color) {
				continue; // 같은 색이면 나가리
			}
			
			// 5칸 연속 같은색인지 확인
			if (map[r+dr[k]][c+dc[k]] != color 
				|| map[r+dr[k]*2][c+dc[k]*2] != color
				|| map[r+dr[k]*3][c+dc[k]*3] != color
				|| map[r+dr[k]*4][c+dc[k]*4] != color) {
				continue; // 다른 색이면 나가리
			}
			
			// 6번째 칸(5칸 이후칸)이 다른색인지 확인
			if (map[r+dr[k]*5][c+dc[k]*5] == color) {
				continue; // 같은 색이면 나가리
			}
			
			return color; // 색깔 리턴 (1:검 2:흰)
		}
		return 0; // 오목아님
	}
	
	
} // end of class

 

입력 및 출력

입력 : 바둑없으면0 검은바둑알1 흰바둑알2

출력 : (첫줄) 검은돌이기면1 흰돌이기면2

        (둘째줄) 오목중 가장 왼쪽에 있는 바둑알 좌표 출력

 

 

컨셉

배열을 순회하면서,

순회하는 해당원소의 우상, 우, 우하, 하를 탐색

 

 

감동포인트

 

1

		// 원래 19이지만 2칸을 추가해준다는 의미를 전달 하기 위해  +2로 표현
		// 테두리를 둘렀을 때의 장단점 
		// 장점 : 가장자리 범위를 넘어가는 것에 대해서 조건 입력 x
		// 단점 : 메모리 낭비, (0,0)이 아니라 (1,1)부터 시작하는 거니까 인덱스에 대한 오류를 범할 수 o
		map = new int[19 + 2][19 + 2];

 

2

		// string 을 잘라서 배열에 저장하는 방법
		/*for (int i = 1; i <= 19; i++) {
			st = new StringTokenizer(br.readLine(), " ");
			for (int j = 0; j < map[i].length; j++) {
				map[i][j] = Integer.parseInt(st.nextToken());
			}
		}*/
		
		// split 나 StringTokenizer 로 자르지 않고 받는 방법 (시간 절약 차원)
		for (int i = 1; i <= 19; i++) {
			String line = br.readLine();
			for (int j = 1, index = 0; j < 19; j++, index=index+2) {
				// - '0' 을 하지 않아도 된다 (시간 절약 차원)
				map[i][j] = line.charAt(index) - '0';
			}
		}

 

3

		// 배열이 잘 만들어 졌는지 확인 (단계별 확인 필요)
		/*for (int i = 0; i < map.length; i++) {
			System.out.println(Arrays.toString(map[i]));
		}*/

 

4

	private static int[] dr = {-1, 0, 1, 1}; // 우상, 우, 우하, 하
	private static int[] dc = {1, 1, 1, 0}; // 우상, 우, 우하, 하

 

5 ★

	/** r,c 좌표에서 시작하는 오목인지 확인, 오목성공이면 오목색상을 리턴 (1:검 2:흰 0:오목아님) */
	private static int complete(int r, int c) {
		
		int color = map[r][c];
		for (int k = 0; k < dc.length; k++) { // 4가지 오목방향
			
			// 이전칸이 나랑 다른 색인지 확인
			if (map[r-dr[k]][c-dc[k]] == color) {
				continue; // 같은 색이면 나가리
			}
			
			// 5칸 연속 같은색인지 확인
			if (map[r+dr[k]][c+dc[k]] != color 
				|| map[r+dr[k]*2][c+dc[k]*2] != color
				|| map[r+dr[k]*3][c+dc[k]*3] != color
				|| map[r+dr[k]*4][c+dc[k]*4] != color) {
				continue; // 다른 색이면 나가리
			}
			
			// 6번째 칸(5칸 이후칸)이 다른색인지 확인
			if (map[r+dr[k]*5][c+dc[k]*5] == color) {
				continue; // 같은 색이면 나가리
			}
			
			return color; // 색깔 리턴 (1:검 2:흰)
		}
		return 0; // 오목아님
	}

 

항상 사방탐색이랑 deltas 부분을 알듯말듯 어렵다고 생각해서 고민이 많았는데 정말 훌륭하고 친절하신 보충 교수님을 만나서 이 부분을 이전보다 더 이해할 수 있게 되었다.. ㅠㅠ for문에서 k인덱스를 사용해서 앞서 정의해준 dr과 dc를 차근차근 순회하면서 map의 인덱스를 요리조리 바꿔주는 것을 보면서, 그래.. 이런 설명이 필요했던거지! 라는 감동의 도가니.. 특히 딱 처음 교수님이 map 인덱스 부분에*2를 입력하는 순간 저게 뭐지? 싶었는데, 이해하는 순간 또 감동의 도가니..

 

마지막 보충이기도 했고, 교수님이 문제해석이나 풀이 이외에 엄청 다양한 미세팁을 오늘 대방출해 주셔서 수업 듣는 내내 너무 감사하기도 하고 감동적이였다(?)! 수업 들으면서도, 보충시간에 다룬 다른 문제나 소스코드는 업로드도 못하고 복습고 못하고 다루지도 못했지만, 이거만큼은 꼭 올려야겠다는 생각을 했다.. 다음 보충 교수님도 좋은 분을 만날 수 있었으면 좋겠다.