카테고리 없음

16929_Two Dots

베리영young 2024. 6. 19. 02:09

사용 알고리즘: bfs

사용 언어: java

 

package afterMiracom_0618;

import java.awt.Point;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Queue;
import java.util.Scanner;

public class Main_bj_16929_TwoDots {
	
	static int n, m;
	static char[][] map;
	static int[] dx = {0,1,0,-1}; //오른쪽, 아래쪽 , 왼쪽, 위쪽
	static int[] dy = {1,0,-1,0};

	public static void main(String[] args) {
		Scanner sc = new Scanner(System.in);
		
		n = sc.nextInt();
		m = sc.nextInt();
		
		map = new char[n][m];
		for(int i=0; i<n; i++) {
			String s = sc.next();
			for(int j=0; j<m; j++) {
				map[i][j] = s.charAt(j);
			}
		}
		//입력 끝
		
		boolean isCycle = startCycle();
		
		if(isCycle) System.out.println("Yes");
		else System.out.println("No");
	}

	private static boolean startCycle() {
		for(int i=0; i<n; i++) {
			for(int j=0; j<m; j++) {
				
				if (canCycle(i, j)) {
					return true;
				}
			}
		}
		return false;
	}

	private static boolean canCycle(int x, int y) {
		Queue<Path> q = new ArrayDeque<>();
		
		ArrayList<Point> al = new ArrayList<>();
		al.add(new Point(x, y));
		
		boolean[][] visit = new boolean[n][m];
		
		q.add(new Path(new Point(x, y), al, visit));
		char color = map[x][y];
		
		while(!q.isEmpty()) {
			Path p = q.poll();
			
			if(p.al.size() == 1) { //이때는 오른쪽으로만 움직여야 함
				int nx = p.point.x + dx[0];
				int ny = p.point.y + dy[0];
				
				if (withinRange(nx, ny) && !visit[nx][ny] && color==map[nx][ny]) {
					visit[nx][ny] = true;
					al.add(new Point(nx, ny));
					q.add(new Path(new Point(nx, ny), al, visit));
				}
			}
			else if(p.al.size() == 2) { //이때는 오른쪽, 아래쪽으로만 움직여야 함
				for(int d=0; d<2; d++) {
					int nx = p.point.x + dx[d];
					int ny = p.point.y + dy[d];
					
					if (withinRange(nx, ny) && !visit[nx][ny] && color==map[nx][ny]) {
						visit[nx][ny] = true;
						al.add(new Point(nx, ny));
						q.add(new Path(new Point(nx, ny), al, visit));
					}
				}
			}
			else { //4방으로 움직일 수 있음 && 사이클 확인
				if(p.al.get(0).equals(p.point)) { //사이클 완성
					return true;
				}
				
				for(int d=0; d<4; d++) {
					int nx = p.point.x + dx[d];
					int ny = p.point.y + dy[d];
					
					if (withinRange(nx, ny) && !visit[nx][ny] && color==map[nx][ny]) {
						visit[nx][ny] = true;
						al.add(new Point(nx, ny));
						q.add(new Path(new Point(nx, ny), al, visit));
					}
				}
			}
			
		}
		return false;
	}

	private static boolean withinRange(int nx, int ny) {
		if(nx>=0 && ny>=0 && nx<n && ny<m) return true;
		return false;
	}

}

class Path {
	Point point;
	ArrayList<Point> al;
	boolean[][] visit;
	
	public Path(Point point, ArrayList<Point> al, boolean[][] visit) {
		this.point = point;
		this.al = al;
		this.visit = visit;
	}
}

 

인사이트:

처음에 boolean[] visit배열로만 했다가 모든 예시에서 사이클이 가능하다고 나왔다.

이유: 시작점에서 한 번 움직인 이후, 4방 탐색을 하면 시작점으로 되돌아 갈 수 있으니까 무조건 사이클이 가능하다고 나온다

해결방법: al에 움직인 경로를 저장해두면서, al의 사이즈가 1일때 (=시작점만 방문했을 때)는 오른쪽으로만 가게 함, al의 사이즈가 2일때(=시작점에서 오른쪽으로 움직였을 때)는 오른쪽이나 아래쪽으로만 가게 함, 그 외 경우는 사이클이 되나 확인하고 + 4방 전부 탐색 가능

이게 되는 이유: x좌표가 작은 순 -> y좌표가 작은 순 으로 시작점을 가지기 때문에 오른쪽->아래쪽으로 탐색하는 게 맞다 (혹은 아래쪽 -> 오른쪽)