πŸ“ Coding Test Study/Algorithm Problem

[C++][Baekjoon][BFS 탐색] 2206번 λ²½ λΆ€μˆ˜κ³  μ΄λ™ν•˜κΈ° 상세 ν•΄μ„€

ibelieveinme 2021. 10. 16. 03:20
728x90

[문제]

 

 

2206번: λ²½ λΆ€μˆ˜κ³  μ΄λ™ν•˜κΈ°

N×M의 ν–‰λ ¬λ‘œ ν‘œν˜„λ˜λŠ” 맡이 μžˆλ‹€. λ§΅μ—μ„œ 0은 이동할 수 μžˆλŠ” 곳을 λ‚˜νƒ€λ‚΄κ³ , 1은 이동할 수 μ—†λŠ” 벽이 μžˆλŠ” 곳을 λ‚˜νƒ€λ‚Έλ‹€. 당신은 (1, 1)μ—μ„œ (N, M)의 μœ„μΉ˜κΉŒμ§€ μ΄λ™ν•˜λ € ν•˜λŠ”λ°, μ΄λ•Œ μ΅œλ‹¨ 경둜

www.acmicpc.net

N×M의 ν–‰λ ¬λ‘œ ν‘œν˜„λœ λ§΅μ—μ„œ (1, 1)μ—μ„œ (N, M)κΉŒμ§€ 이동할 λ•Œ κ±Έλ¦¬λŠ” μ΅œλ‹¨ 경둜λ₯Ό κ΅¬ν•˜λŠ” 문제.

이 λ•Œ, 0은 이동할 수 μžˆλŠ” 곳을 λ‚˜νƒ€λ‚΄κ³ , 1은 이동할 수 μ—†λŠ” 벽이 μžˆλŠ” 곳을 λ‚˜νƒ€λ‚΄λ©° 1νšŒμ— ν•œν•˜μ—¬ 벽을 λΆ€μˆ˜κ³  이동할 수 μžˆλ‹€.

 

[풀이]

Break 유무λ₯Ό μ €μž₯해놓고 ν•œλ²ˆλ„ κΉ¬ 적 μ—†λŠ” 벽을 κΉ¨κ°€λ©΄μ„œ 탐색해야 ν•  것 같은데 κ·Έ ꡬ쑰λ₯Ό λͺ»μ§œκ² μ–΄μ„œ ν•œμ°Έμ„ ν—€λ§Έλ‹€.

κ²°κ΅­ 풀이 μ°Έμ‘°...

 

이 문제의 핡심은 νƒμƒ‰ν•œ 경둜λ₯Ό queue에 μ €μž₯ν•  λ•Œ, 벽을 λΆ€μˆœ κ²½λ‘œμΈμ§€ 벽을 λΆ€μˆ˜μ§€ μ•Šμ€ κ²½λ‘œμΈμ§€λ„ μ €μž₯ν•΄μ€˜μ•Ό ν•œλ‹€λŠ” 점이닀. λ˜ν•œ, νƒμƒ‰ν•œ 경둜λ₯Ό 방문체크할 λ•Œ 벽을 λΆ€μˆœ 적이 μžˆμœΌλ©΄μ„œ λ°©λ¬Έν•  μˆ˜λ„ 있고 벽을 λΆ€μˆœμ μ΄ μ—†μœΌλ©΄μ„œ λ°©λ¬Έν•  μˆ˜λ„ 있기 λ•Œλ¬Έμ— 두 κ°€μ§€ 경우의 수λ₯Ό λͺ¨λ‘ 열어두고 ν•œ 경우만 λ°©λ¬Έ μ²΄ν¬ν•΄μ€˜μ•Όν•œλ‹€λŠ” 점이닀.

 

이 λ•Œ, 벽을 λΆ€μˆœ 적이 μžˆλ‹€λ©΄ 벽이 μ•„λ‹Œ 경둜λ₯Ό κ³„μ†ν•΄μ„œ μ΄λ™ν•˜λ©΄ 되고, 벽을 λΆ€μˆœ 적이 μ—†λ‹€λ©΄ λ²½κ³Ό 벽이 μ•„λ‹Œ κ³³ λͺ¨λ‘  이동가λŠ₯ν•˜λ‹€.

 

즉, 각 점듀은 μ’Œν‘œμ™€ 벽을 λΆ€μˆœ 횟수, 이동 횟수 정보λ₯Ό κ°–κ³  μžˆμ–΄μ•Ό ν•œλ‹€. 이동 횟수 μ •λ³΄λŠ” μ΅œλ‹¨κ²½λ‘œκΈΈμ΄λ₯Ό μ°Ύμ•„μ•Όν•˜κΈ° λ•Œλ¬Έμ— μ €μž₯해쀬닀. 탐색할 λ•Œλ§ˆλ‹€ +1μ”©ν•΄μ„œ 경둜 길이λ₯Ό μ°ΎλŠ”λ‹€.

class Point {
public:
	int x;
	int y;
	int breakCount;//벽을 λΆ€μˆœ 횟수. 0 or 1둜 ν‘œν˜„
	int count;//이동 횟수. λ‚˜μ€‘μ— 좜λ ₯ν•΄μ€˜μ•Ό 함.
	Point(int x, int y, int breakCount, int count) {
		this->x = x;
		this->y = y;
		this->breakCount = breakCount;
		this->count = count;
	}
};

그리고 벽을 λš«μ€ 적이 μžˆλŠ” κ²½λ‘œκ°€ 방문체크λ₯Ό ν•˜λ©° νƒμƒ‰ν•˜λŠ” κ²½μš°μ™€ 벽을 λš«μ€ 적 μ—†λŠ” κ²½λ‘œκ°€ λ°©λ¬Έμ²΄ν¬ν•˜λ©° νƒμƒ‰ν•˜λŠ” 경우. 2κ°€μ§€λ‘œ λ‚˜λ‰˜κΈ° λ•Œλ¬Έμ— visited  배열을 2개 λ§Œλ“€μ–΄μ€˜μ•Ό ν•œλ‹€. κ·Έ 배열은 3차원 ν–‰λ ¬λ‘œ ν‘œν˜„ν–ˆλ‹€. 벽을 λš«μ€ 적 μ—†μœΌλ©΄μ„œ λ°©λ¬Έν–ˆλ‹€λ©΄ visited[x][y][0] = true둜, λ²½μ„ λš«μ€ 적 μžˆμœΌλ©΄μ„œ λ°©λ¬Έν–ˆλ‹€λ©΄ visited[x][y][1] = true 둜 체크할 κ±°λ‹€.

bool visited[MAX_VALUE][MAX_VALUE][2] = { false, };

 

 

μ—¬κΈ°μ„œ 질문이 μžˆμ„ 것이닀. (λ‚˜λ„ 계속 이해가 μ•ˆκ°”λ˜ λΆ€λΆ„!!) 벽을 λš«μ„ λ•Œ, 항상 μ΅œμ„ μ΄ λ˜λŠ” 경우λ₯Ό λš«μ–΄μ•Ό ν•  텐데, μ•žμ—μ„œ λš«μ€ κ²½μš°λ³΄λ‹€ λ’€μ—μ„œ λš«μ€ κ²½μš°κ°€ 더 μ΅œμ„ μ΄λ©΄ μ–΄λ–‘ν•˜μ§€????

 

결둠은 상관없닀 !!! 뒀에 λ‚˜μ˜€λŠ” μ΅œμ„ μ˜ κ²½μš°λŠ” 벽을 λš«μ€ 적 μ—†λŠ” κ²½λ‘œκ°€ μž¬νƒμƒ‰ν•΄μ€„ 것이기 λ•Œλ¬Έμ΄λ‹€ !!!!!

μ•„λž˜ 예제λ₯Ό 보며 μ΄ν•΄ν•΄λ³΄μž.

 

예제 1λ²ˆμ„ 풀어보면 λ‹€μŒκ³Ό κ°™λ‹€.

queue에 μ €μž₯λ˜λŠ” 인자 3κ°œλŠ” Point정보λ₯Ό μ˜λ―Έν•˜λŠ”λ° μˆœμ„œλŒ€λ‘œ Point(xμ’Œν‘œ, yμ’Œν‘œ, 벽을 λΆ€μˆœ 횟수)이닀.

즉, 맨 μ²˜μŒμ— 큐에 μ €μž₯λ˜λŠ” 값은 xμ’Œν‘œ 1, yμ’Œν‘œ 1, 벽을 λΆ€μˆœ 횟수 0을 μ €μž₯ν•œλ‹€.

 

그리고 (1, 1)λΆ€ν„° 였λ₯Έμͺ½, μ•„λž˜, μ™Όμͺ½, μœ„ 순으둜 주변을 νƒμƒ‰ν•œλ‹€.

(1, 1) μ£Όμœ„ μ’Œν‘œμΈ (1, 2)와 (2, 1)을 νƒμƒ‰ν•˜λŠ”λ°, 아직 벽을 λΆ€μˆœ νšŸμˆ˜κ°€ 0μ΄λ―€λ‘œ 벽을 λ§Œλ‚¬μ„ λ•Œ 벽을 λΆ€μˆ  수 μžˆλ‹€.

λ”°λΌμ„œ 벽을 λΆ€μˆ˜κ³  λ‹€μŒ 경둜둜 이동할 것이닀. 즉, visited[1][2][1] = visited[2][1][1] = true둜 λ°©λ¬Έ 체크해주고 queue에 Point(1, 2, 1)κ³Ό Point(2, 1, 1)을 λ„£λŠ”λ‹€.

 

λ‹€μŒ queue의 맨 μ•žμ— μžˆλŠ” Point(1, 2, 1)을 κΊΌλ‚΄μ„œ 탐색을 μ§„ν–‰ν•  건데,

이미 μ•žμ—μ„œ 벽을 λΆ€μˆœ 적이 μžˆμœΌλ―€λ‘œ(3번째 μΈμžκ°€ 1μ΄λ―€λ‘œ) λ‹€μŒ κ²½λ‘œμ—μ„  벽이 μ•„λ‹Œ 경둜(map[x][y] = 0)만 탐색이 κ°€λŠ₯ν•˜λ‹€. 그리고 벽을 λΆ€μˆœ 적이 μžˆμœΌλ―€λ‘œ breakedCountλ₯Ό +1 ν•΄μ€€ μƒνƒœμΈ Point(1, 3, 1) λ₯Ό queue에 μ €μž₯ν•˜κ³  벽을 λΆ€μˆ˜κ³  λ°©λ¬Έν–ˆλ‹€λŠ” ν‘œμ‹œλ‘œ visited[1][3][1] = true둜 λ°©λ¬Έ 체크 ν•΄μ€€λ‹€.

κ·Έ λ’€λ‘œλ„ 벽을 λΆ€μˆ  수 μžˆλŠ” νšŸμˆ˜κ°€ 0μ΄λ―€λ‘œ 벽이 μ•„λ‹Œ 경둜만 νƒμƒ‰ν•˜λ©° (N, M) μ’Œν‘œμ— 도달할 것이닀.

 

λ‹€λ₯Έ μ˜ˆμ œλ„ μ‚΄νŽ΄λ³΄μž.

μœ„ μ˜ˆμ œλŠ” 맨 μ²˜μŒλΆ€ν„° 벽을 λš«μ§€ μ•ŠλŠ”λ‹€. 벽을 λš«κ±°λ‚˜ λš«μ§€ μ•ŠμœΌλ©΄μ„œ N x M μœ„μΉ˜κΉŒμ§€ κ°€λŠ” κ±°λ‹€.

 

Point(1, 1, 0) μ’Œν‘œλ₯Ό νƒμƒ‰ν•˜κ³ , Point(1, 2, 0) μ’Œν‘œμ™€ Point(2, 1, 0) μ’Œν‘œλ₯Ό 큐에 λ„£λŠ”λ‹€. 그리고 visited[1][2][0] = visited[2][1][0] = true둜 λ°©λ¬Έ μ²΄ν¬ν•œλ‹€. 아직 벽을 λš«μ€ 적이 μ—†μœΌλ―€λ‘œ 두 μ’Œν‘œ λͺ¨λ‘ 3번째 인자(벽을 λš«μ€ 횟수)λŠ” 0이이고 벽을 λš«μ§€ μ•Šκ³  μ΄λ™ν•œ κ²½λ‘œμ— λ°©λ¬Έμ²΄ν¬ν•΄μ£ΌλŠ” κ±°λ‹€.

 

λ‹€μŒ queue의 제일 μ•žμ— μžˆλŠ” μ’Œν‘œμΈ Point(1, 2, 0)λ₯Ό νƒμƒ‰ν•˜μž. 이 λ•ŒλŠ” (1, 3)μ’Œν‘œμ™€ (2, 2)μ’Œν‘œλ₯Ό 탐색할 수 μžˆλŠ”λ°, (1, 3)μ’Œν‘œλ‘œ 이동할 κ²½μš°λŠ” 벽을 λš«μ§€ μ•Šκ³  이동할 수 있고, (2, 2)μ’Œν‘œλ‘œ 이동할 λ•ŒλŠ” 벽을 λš«μ–΄μ•Όλ§Œ 이동 κ°€λŠ₯ ν•˜λ‹€. Point(1, 2, 0) κ°’μ˜ 3번째 인자λ₯Ό 톡해 이 κ²½λ‘œλŠ” 벽을 λš«μ€ 적이 μ—†μœΌλ―€λ‘œ 두 μ’Œν‘œ λͺ¨λ‘ queue에 μ €μž₯ν•œλ‹€. 이 λ•Œ, (1, 3)으둜 μ΄λ™ν•˜λŠ” κ²½λ‘œλŠ” 벽을 λš«μ€ 적 μ—†μœΌλ―€λ‘œ Point(1, 3, 0)의 값을, (2, 2)둜 μ΄λ™ν•˜λŠ” κ²½λ‘œλŠ” 벽을 λš«μ–΄μ•Ό ν•˜λ―€λ‘œ Point(2, 2, 1)둜 ν‘œν˜„ 및 queue에 μ €μž₯ν•œλ‹€. 그리고 visited[1][3][0] = visited[2][2][1] =  true 둜 각각 벽을 λš«μ€ 적 μ—†μœΌλ©΄μ„œ λ°©λ¬Έν•œ κ²½λ‘œμ™€ 벽을 λš«μœΌλ©΄μ„œ λ°©λ¬Έν•œ 경둜λ₯Ό 방문체크해쀀닀.

λ‹€μŒ queue의 맨 μ•žμ˜ 값인 Point(2, 1, 0)을 νƒμƒ‰ν•˜μž. Point(2, 1, 0)의 값을 보면 μ•Œ 수 μžˆλ“œμ‹œ μ§€κΈˆκΉŒμ§€ 벽을 λš«μ€ μ μ—†λŠ” 경둜λ₯Ό μ €μž₯ν•œ 것이닀. 아직 벽을 λš«μ€ 적이 μ—†μœΌλ―€λ‘œ (2, 2)μ’Œν‘œμ™€ (3,1)μ’Œν‘œ λͺ¨λ‘ κ°€λŠ₯ν•  텐데, (2, 2) μ’Œν‘œλ‘œ 이동할 경우 벽을 λš«μ€ 적 μžˆλ‹€λŠ” ν‘œμ‹œλ₯Ό ν•΄μ€˜μ•Ό ν•œλ‹€. Point(2, 2, 1) μ΄λ ‡κ²Œ. 근데 (2, 2, 1)은 이미 (1, 2)μ’Œν‘œμ—μ„œ 탐색할 λ•Œ visited[2][2][1] = true둜 체크해주고 queue에 넣어쀬닀. λ”°λΌμ„œ 이미 이번 κ²½λ‘œμ—μ„œλŠ” Point(3, 1, 0)만 μ €μž₯ν•˜κ³  벽을 λš«μ€ 적 μ—†μœΌλ©΄μ„œ λ°©λ¬Έν–ˆλ‹€λŠ” ν‘œμ‹œλ‘œ visited[3][1][0] = true 둜 체크해쀀닀.

 

κ·Έ λ‹€μŒμ€ Point(1,3,0)λ₯Ό νƒμƒ‰ν•œλ‹€. (1, 4) μ’Œν‘œμ™€, (2, 3) μ’Œν‘œλ₯Ό 탐색할 텐데, (1, 4)μ’Œν‘œλŠ” 벽이 μ•„λ‹Œ 경둜λ₯Ό μ²΄ν¬ν•˜λŠ” 배열에 방문체크해주고, (2, 3)μ’Œν‘œλŠ” 벽을 λš«μ–΄μ•Ό ν•˜λ―€λ‘œ 벽인 경둜λ₯Ό μ²΄ν¬ν•˜λŠ” 배열에 방문체크해쀀닀. 즉 visited[1][4][0] = visited[2][3][1] = true이고, queue에 Point(1, 4, 0)κ³Ό Point(2, 3, 1) 값이 μ €μž₯λœλ‹€.

Point(2, 2, 1)은 벽을 λΆ€μˆœ 적 μžˆλŠ” μ΄λ™κ²½λ‘œ μ΄λ―€λ‘œ visited[2][3][1] = visited[3][2][1]= true둜 체크해주고, Point(2, 3, 1)κ³Ό Point(3, 2, 1)을 queue에 λ„£λŠ”λ‹€.

 

Point(3, 1, 0)은 벽을 λΆ€μˆœ 적 μ—†λŠ” μ΄λ™κ²½λ‘œ μ΄λ―€λ‘œ visited[3][2][0] = visited[4][1][0] = true둜 체크해주고 queue에 Point(3, 2, 0)κ³Ό Point(4, 1, 0)을 λ„£μ–΄μ€€λ‹€.

 

μ—¬κΈ°μ„œ queue에 Point(3, 2, 1)κ³Ό Point(3, 2, 0)이 μžˆλŠ” 것이 보일 것이닀. μ΄λ ‡κ²Œ 벽을 λΆ€μˆ˜λ©° νƒμƒ‰ν•˜λŠ” κ²½λ‘œμ™€ 벽을 λΆ€μˆ˜μ§€ μ•Šκ³  νƒμƒ‰ν•˜λŠ” 경우 λ‘˜λ‹€λ₯Ό ν™•μΈν•˜κΈ° λ•Œλ¬Έμ— λ‘˜ λ‹€μ˜ κ²½μš°μ—μ„œ μ΅œλ‹¨κ²½λ‘œλ₯Ό νŒŒμ•…ν•  수 μžˆλŠ” 것이닀.

κ·Έ λ’€λ‘œ μœ„ 과정을 계속 λ°˜λ³΅ν•œλ‹€.

queue의 값이 λͺ¨λ‘ λΉ μ§€κ³  ν˜„μž¬ μ’Œν‘œκ°€ (N,M)이 되면 ν˜„μž¬κΉŒμ§€ κ΅¬ν•œ 경둜길이λ₯Ό λ°˜ν™˜ν•΄μ€€λ‹€!

 

[μ½”λ“œ]

#include <iostream>
#include <queue>
#include <algorithm>
using namespace std;

const int MAX_VALUE = 1001;
int direction[4][2] = { {0,1}, {1,0},{0,-1},{-1,0} };//이동 λ°©ν–₯ν‚€: 였λ₯Έμͺ½, μ•„λž˜, μ™Όμͺ½, μœ„

class Point {
public:
	int x;
	int y;
	int breakCount;//벽을 λΆ€μˆœ 횟수. 0 or 1둜 ν‘œν˜„
	int count;//이동 횟수. λ‚˜μ€‘μ— 좜λ ₯ν•΄μ€˜μ•Ό 함.
	Point(int x, int y, int breakCount, int count) {
		this->x = x;
		this->y = y;
		this->breakCount = breakCount;
		this->count = count;
	}
};

int MoveByBFS(int N, int M, int map[MAX_VALUE][MAX_VALUE]);//BFSλ₯Ό ν†΅ν•œ μ΅œλ‹¨κ±°λ¦¬ μ°ΎκΈ° μˆ˜ν–‰

int main() {
	int N, M;
	cin >> N >> M;
	int map[MAX_VALUE][MAX_VALUE] = { 0, };
	for (int i = 0; i < N; i++) {
		for (int j = 0; j < M; j++) {
			scanf_s("%1d", &map[i][j]);
		}
	}
	cout << MoveByBFS(N, M, map);
	return 0;
}


int MoveByBFS(int N, int M, int map[MAX_VALUE][MAX_VALUE]) {
	queue<Point> q;
	bool visited[MAX_VALUE][MAX_VALUE][2] = { false, }; 

	q.push(Point(0,0,0,1));
	visited[0][0][0] = true;//벽을 λΆ€μˆœ νšŸμˆ˜κ°€ 0μ΄λ©΄μ„œ ν•΄λ‹Ή μ’Œν‘œμ— λ°©λ¬Έν–ˆμŒμ„ λ‚˜νƒ€λƒ„
	visited[0][0][1] = true;//벽을 λΆ€μˆœ νšŸμˆ˜κ°€ 1μ΄λ©΄μ„œ ν•΄λ‹Ή μ’Œν‘œμ— λ°©λ¬Έν–ˆμŒμ„ λ‚˜νƒ€λƒ„

	while (!q.empty()) {
		Point currentPoint = q.front();
		q.pop();

		if (currentPoint.x == N - 1 && currentPoint.y == M - 1) {
			return currentPoint.count;
		}
		for (int i = 0; i < 4; i++) {
			int nx = currentPoint.x + direction[i][0];
			int ny = currentPoint.y + direction[i][1];
			if (nx < N && nx >= 0 && ny < M && ny >= 0) {
				if (!visited[nx][ny][0]) {//λ²½ λΆ€μˆœ νšŸμˆ˜κ°€ 0μ΄λ©΄μ„œ ν•΄λ‹Ή μ’Œν‘œμ— λ°©λ¬Έν•œ 적이 μ—†λŠ” 경우
					if (map[nx][ny] == 0 && currentPoint.breakCount == 0) {//ν•΄λ‹Ή μ’Œν‘œμ— 벽이 μ—†κ³  μ΄μ „κΉŒμ§€ λ²½ λΆ€μˆœ νšŸμˆ˜λ„ 0일 경우만 push
						visited[nx][ny][0] = true;
						q.push(Point(nx, ny, 0, currentPoint.count + 1));
					}
				}
				if (!visited[nx][ny][1]) {//λ²½ λΆ€μˆœ νšŸμˆ˜κ°€ 1μ΄λ©΄μ„œ ν•΄λ‹Ή μ’Œν‘œμ— λ°©λ¬Έν•œ 적이 μ—†λŠ” 경우
					if ((map[nx][ny] == 1 && currentPoint.breakCount == 0)//ν•΄λ‹Ή μ’Œν‘œμ— 벽이 있고 λ²½ λΆ€μˆœ νšŸμˆ˜κ°€ 0인 경우
							|| (map[nx][ny] == 0 && currentPoint.breakCount == 1)) {//or ν•΄λ‹Ή μ’Œν‘œμ— 벽이 μ—†κ³  λ²½ λΆ€μˆœ νšŸμˆ˜κ°€ 1인 경우
						visited[nx][ny][1] = true;
						q.push(Point(nx, ny, 1, currentPoint.count + 1));
					}
				}
			}
		}
	}
	return -1;
}
728x90