Algorithm/프로그래머스

프로그래머스 - 아이템 줍기

whyWhale 2023. 4. 16.

문제 설명

여러 사각형의 겉면을 따라 목표지점으로 갈 수 있는 최단 거리를 구하는 문제이다.

주의 사항

단순 선을 격자의 칸으로 생각하고 최단거리를 탐색하게 되면 이슈가 있을 것이다.

프로그래머스 - 아이템 줍기 - 문제 설명

왼쪽 그림 처럼 겉면이 표시된 경우를 보면 우리가 기대하는 탐색과는 다르게 탐색을 하게 된다.

왜냐하면 4방향을 탐색하면서 갈 수 있는지 판단할 것이기 때문이다.

2배율로 격자판을 확장하면 저런 경우를 방지할 수 있다! 

프로그래머스 - 아이템 줍기 - 문제 설명

 

기대하는 바와 다르게 탐색 하는 경우를 방지하기 위해 2배율을 한다면 이런식으로 좌표도 확장 되어 거리가 생기니 기대하는 탐색과 다르게 탐색 되지 않게된다!

프로그래머스 - 아이템 줍기 - 문제 설명

 

또다른 시도로는 각 칸에 갈수 있는 방향을 넣어줬다.(정사각형의 입력을 주어질때)

하지만 해당 방법에서 위 그림의 경우 예외를 방지할 수 있지만 또다른 예외가 발생한다.

테스트 케이스 5번을 보면 이런 예외가 발생한다.

프로그래머스 - 아이템 줍기 - 문제 설명

노랑 격자칸은 오른쪽 왼쪽 위쪽도 갈 수 있다. 그래서 해당 직사각형이 촘촘하게 형성된 경우는 사각형 내부에도 포함이 되지 않을 뿐더러 예외를 잡을 수 없었다….

방향에 우선순위도 고려해봤지만… 결국 짧은 탐색이라 먼저 return 하게 된다..

촘촘한 사각형인 경우는 방향을 넣어주는 방법은 예외를 피해 갈 수 없다.. 이런 부분은 하드 코딩으로 막아줘야 하는데.. 2배율로 하는 것이 훨씬 플랙시블 한 것 같다.

 

 

풀이

	final int UP = 0, DOWN = 1, LEFT = 2, RIGHT = 3;
	int dy[] = {-1, 1, 0, 0};
	int dx[] = {0, 0, -1, 1};

	class Node {
		int y, x, cnt;

		public Node(int y, int x, int cnt) {
			this.y = y;
			this.x = x;
			this.cnt = cnt;
		}

	}

	class Rectangle {
		int y1, x1, y2, x2;

		public Rectangle(int y1, int x1, int y2, int x2) {
			this.y1 = y1;
			this.x1 = x1;
			this.y2 = y2;
			this.x2 = x2;
		}

		public boolean isInner(int ny, int nx) {
			return y1 < ny && y2 > ny && x1 < nx && x2 > nx;
		}
	}

	public int solution(int[][] rectangle, int characterX, int characterY, int itemX, int itemY) {
		Set<Integer>[][] path = new Set[51][51];
		boolean[][] v = new boolean[51][51];

		for(int i=0; i<51; i++){
			for(int j=0; j<51; j++){
				path[i][j]=new HashSet<>();
			}
		}

		List<Rectangle> rects = new ArrayList<>();

		for (int[] r : rectangle) {
			int y1 = r[1];
			int x1 = r[0];
			int y2 = r[3];
			int x2 = r[2];

			// 시계
			path[y1][x1].add(RIGHT);
			path[y1][x2].add(DOWN);
			path[y2][x2].add(RIGHT);
			path[y2][x1].add(UP);
			// 반시계
			path[y1][x1].add(DOWN);
			path[y1][x2].add(LEFT);
			path[y2][x2].add(UP);
			path[y2][x1].add(RIGHT);

			for (int i = x1 + 1; i < x2; i++) {
				path[y1][i].add(RIGHT);
				path[y1][i].add(LEFT);
			}

			for (int i = y1 + 1; i < y2; i++) {
				path[i][x2].add(DOWN);
				path[i][x2].add(UP);
			}

			for (int i = x2 - 1; i > x1; i--) {
				path[y2][i].add(LEFT);
				path[y2][i].add(RIGHT);
			}

			for (int i = y2 - 1; i > y1; i--) {
				path[i][x1].add(UP);
				path[i][x1].add(DOWN);
			}

			rects.add(new Rectangle(y1, x1, y2, x2));
		}

		LinkedList<Node> q = new LinkedList<>();
		q.offer(new Node(characterY, characterX, 0));
		v[characterY][characterX] = true;

		while (!q.isEmpty()) {
			Node cur = q.poll();
			System.out.println(cur.y + ", " + cur.x + " - " + cur.cnt);
			if (cur.y == itemY && cur.x == itemX) {
				return cur.cnt;
			}

			for (int dir : path[cur.y][cur.x]) {
				int ny = dy[dir] + cur.y;
				int nx = dx[dir] + cur.x;

				if (v[ny][nx])
					continue;

				if (isInnerRects(ny, nx, rects))
					continue;
				v[ny][nx] = true;
				q.offer(new Node(ny, nx, cur.cnt + 1));
			}

		}

		return -1;
	}

	boolean isInnerRects(int ny, int nx, List<Rectangle> rects) {
		for (Rectangle r : rects) {
			if (r.isInner(ny, nx)) {
				return true;
			}
		}

		return false;
	}

댓글