inflearn logo
강의

강의

N
챌린지

챌린지

멘토링

멘토링

N
클립

클립

로드맵

로드맵

지식공유

10주완성 C++ 코딩테스트 | 알고리즘 코딩테스트

3-D와 반례

3-D 메모리 초과 질문드립니다

338

qnvr31p

작성한 질문수 6

0

- 학습 관련 질문을 남겨주세요. 상세히 작성하면 더 좋아요!
- 먼저 유사한 질문이 있었는지 검색해보세요.
- 서로 예의를 지키며 존중하는 문화를 만들어가요.
- 잠깐! 인프런 서비스 운영 관련 문의는 1:1 문의하기를 이용해주세요.

http://boj.kr/78f586fab057402f93621ce7d148e6bb

지도를 입력받을 때 탈출 부분을 벡터에 push_back 하고

사람과 불을 각각 bfs 돌렸습니다.

탈출 부분 벡터를 반복문으로 돌면서

탈출이 가능한 경우 그 시간을 ret 벡터에 넣고

ret의 크기가 0이면 impossible을 출력하고

0이 아니면 정렬하여 맨 앞의 숫자를 출력하도록 했는데

메모리 초과가 뜹니다... ㅠㅠ 왜그럴까요?

c++ 코딩-테스트

답변 1

0

큰돌

안녕하세요 qn님 ㅎㅎ

 

이부분에서 문제가 있는 거 같습니다.

디버깅코드를 넣어봤는데요.

	while (q.size()) {
		tie(y, x) = q.front(); q.pop();
		for (int i = 0; i < 4; i++) {
			int ny = y + dy[i];
			int nx = x + dx[i];

			if (ny < r && ny >= 0 && nx < c && nx >= 0 && m[ny][nx] != '#') {
				if (visited[ny][nx] > visited[y][x]) {
					cout << visited[ny][nx] << '\n';
					visited[ny][nx] = visited[y][x] + 1;
					q.push({ ny, nx });
				}
			}
		}
		cout << q.size() << "\n";
	}

이렇게 디버깅 해보시겠어요?

이부분에서 무한루프가 걸리며 q.size()가 계속해서 증가하는 모습이 보입니다.

 

반례

10 100

....................................................................................................

.......###############.......FFFF..........########.................................................

.........FFFFFFFFFFFFF#############...........................FFF..........##########....########...

.....................................................................######..................####...

.......FF...................................J.......................................................

.............................................................................................####...

......................###############################################...............................

....................................................................................................

.........FFFFFFFFFFFFF............####..........................................FFFFFF....FFFFF.....

.............................................###.######.............................................

 

답 : 7

 

해당 부분에 반례를 해결하려면

이런식으로 바꿔야 하지 않을까요? 방문을 안했다면 >> 방문처리

				if (visited[ny][nx] == 2e9) { 

 

그리고 이 반례 부분을 해결 못하고 있습니다.

5 5
FFFFF
..J..
.....
.....
.....

답 : 4

이 부분은

	for (auto a : fire) {
		bfs(a.first, a.second);
	}

이런식으로 fire마다 순차적으로 bfs를 돌려서 그런데요. fire부분을 q에 담아서 bfs를 한꺼번에 돌려야 하지 않을까요?

 

감사합니다.

0

qnvr31p

http://boj.kr/972b9dbcb4b24d56a34b37744dde4aec

 

원래 fire마다 순차적으로 bfs를 돌리는 것이 목표였습니다. 순차적으로 bfs를 진행하면서

이전의 불들이 현재 위치까지 걸리는 시간(visited에 계속 기록)보다

현재 돌리는 fire가 현재 위치에 도달하는 시간이 더 적을 경우

그 적은 시간을 갱신하는 식으로 visited를 완성해나가고자 했습니다.

 

그 후 지훈이가 이동하는 거리를 visited2에 기록한 후 벡터에 넣어놨던 탈출 위치를

반복문을 통해 돌면서 지훈 이동 거리(visited2)가 불 거리(visited)보다 작을 경우

지훈이가 해당 탈출 위치에 더 빨리 도착한다는 것이므로 그 거리들을 벡터에 넣어놓고

정렬하여 가장 작은 거리를 마지막에 출력하고자 했습니다.

 

이전 시도했던 코드는 bfs 함수에서 if (visited[ny][nx] > visited[y][x]) 이 부분 때문에

계속 큐 사이즈가 올라 갔습니다. 어쩌피 ny, nx는 현재 위치에서 상하좌우 한 칸씩 이동한 곳이기 때문에 그 차이가 한 칸이라면 탐색할 이유가 없어서(같은 거리이므로) if (visited[ny][nx] > visited[y][x] + 1)로 조건을 고쳤습니다.(위 링크)

 

근데 이렇게 수정하니 메모리 초과는 안뜨지만 여전히 에러가 나옵니다.

어떤 반례가 있는 걸까요....?

0

큰돌

안녕하세요 ㅎㅎ

 

원래 fire마다 순차적으로 bfs를 돌리는 것이 목표였습니다. 순차적으로 bfs를 진행하면서

이전의 불들이 현재 위치까지 걸리는 시간(visited에 계속 기록)보다

현재 돌리는 fire가 현재 위치에 도달하는 시간이 더 적을 경우

그 적은 시간을 갱신하는 식으로 visited를 완성해나가고자 했습니다.

>> 해당 로직은 이해했는데요. ㅎㅎ 근데 사실 그러한 로직은 비효율적입니다.

			if (ny < r && ny >= 0 && nx < c && nx >= 0 && m[ny][nx] != '#') {
				if (visited[ny][nx] > visited[y][x] + 1) {

bfs는 방문한 정점은 다시 방문하지 않는다. 라는 특징을 가지고 해당 부분 때문에 빠릅니다. 근데 이 코드는 방문한 정점을 다시 방문하게 되죠.

즉, 비효율적이게 됩니다.

이를 queue에다가 시작 F정점들을 담아두고 단한번만 bfs를 돌리게 수정하는게 좋습니다.

어떤 반례가 있는 걸까요....?

>> 반례는 몇개 시도해봤는데 잘 안나오네요...

찾는대로 답변 드리겠습니다.

감사합니다.

0

큰돌

반례 찾았습니다. ㅎㅎ

예를 들어
FF..

이렇게 되어있을 때

오른쪽 F부터 bfs가 시작되면

2123

이렇게 visited배열이 완성되겠죠?

근데 여기서

왼쪽 F이 시작되면

1234

이렇게 되어버리지 않을까요?

 

원래는 이게 맞을 거 같습니다.

1123

 

 

감사합니다.

0

qnvr31p

항상 빠른 답변 감사합니다!

 

2123 상태에서 왼쪽 F가 시작되면 bfs를 호출해서 우선 1123이 될테고(while문 이전 초기화)

while문에 들어가면 if (visited[ny][nx] > visited[y][x] + 1) 조건을 만족시키지 못하기 때문에

더이상 큐에 push가 되지 않고 1123이 그대로 유지될 것 같은데요...?

 

 image

실제로 돌려보면 0012로 출력됩니다.(전 시작할 때 0으로 지정해서 1씩 빠진 값으로 출력됩니다)

 

0

큰돌

더이상 큐에 push가 되지 않고 1123이 그대로 유지될 것 같은데요...?

>> 네 그러네요..ㅜㅜ...이게 최솟값으로 갱신되서 그러는거 같습니다.

 

하하 하지만 반례를 찾았습니다.

 

3 3

.FF

..J

...

답 : 1

수강생님 코드 : 2

 

 

감사합니다.

0

qnvr31p

else if를 잘못 설정해서 지훈이가 탈출 지역에 있을 때

탈출점을 push_back할 때 누락되는 부분이 있었군요

 

해당 부분을 아래와 같이 고쳤습니다.

for (int i = 0; i < r; i++) {

for (int j = 0; j < c; j++) {

cin >> m[i][j];

if (m[i][j] == 'J') pos = { i,j };

else if (m[i][j] == 'F') fire.push_back({ i, j });

if (i == r - 1 || j == c - 1 || i == 0 || j == 0) {

if (m[i][j] != '#') {

esc.push_back({ i,j });

}

}

}

}

 

드디어! 를 외치며 제출을 했는데 어림도 없었습니다....

이전과 같이 계속 2%에서 틀렸습니다 가 나오네요 ㅠㅠ

 

사실 강의보고 해당 알고리즘 대로 코드 구성해서 이미 해결한 문제이긴 한데

처음 생각했던 알고리즘도 말이 된다 싶어서 계속 시도해보고 있어요...

계속 반례가 나오는데 그만 놓아주어야 할까요...?

 

0

큰돌

넴.. 놓아주어야 할 것 같습니다.

사실 저도 여러번 시도를 하고 있는데요.

이상하게 안되네요..

제가 qn님 코드 수정한 부분은 다음과 같습니다.

#include <bits/stdc++.h>
using namespace std;
const int dy[] = { -1, 0, 1, 0 };
const int dx[] = { 0, 1, 0, -1 };
int r, c, cnt;
char m[1004][1004];
int visited[1004][1004];
int visited2[1004][1004];
vector<pair<int, int>> fire;
vector<pair<int, int>> esc;
vector<int> ret;

void bfs2(int y, int x) {
	visited2[y][x] = 0;
	queue<pair<int, int>> q;
	q.push({ y, x });

	while (q.size()) {
		tie(y, x) = q.front(); q.pop();
		for (int i = 0; i < 4; i++) {
			int ny = y + dy[i];
			int nx = x + dx[i];

			if (ny < r && ny >= 0 && nx < c && nx >= 0 && m[ny][nx] != '#' && visited2[ny][nx] == -1) {
				visited2[ny][nx] = visited2[y][x] + 1;
				q.push({ ny, nx });
			}
		}
	}

}

void bfs() {
	int y, x;
	queue<pair<int, int>> q;
	for (auto a : fire) {
		tie(y, x) = a;
		visited[y][x] = 0; 
		q.push({ y, x }); 
	} 
	while (q.size()) {
		tie(y, x) = q.front(); q.pop(); 
		for (int i = 0; i < 4; i++) {
			int ny = y + dy[i];
			int nx = x + dx[i];

			if (ny < r && ny >= 0 && nx < c && nx >= 0 && m[ny][nx] != '#' && visited[ny][nx] == 2e9) { 
					visited[ny][nx] = visited[y][x] + 1;
					q.push({ ny, nx });
			} 
		}

		//cout << q.size() << "\n";
	}

}

int main() {
	ios_base::sync_with_stdio(false);
	cin.tie(NULL);
	cout.tie(NULL);
	fill(&visited[0][0], &visited[0][0] + 1004 * 1004, 2e9);
	fill(&visited2[0][0], &visited2[0][0] + 1004 * 1004, -1);
	cin >> r >> c;

	pair<int, int> pos;

	for (int i = 0; i < r; i++) {
		for (int j = 0; j < c; j++) {
			cin >> m[i][j];
			if (m[i][j] == 'J') pos = { i,j };
			else if (m[i][j] == 'F') fire.push_back({ i, j });
			else if (i == r - 1 || j == c - 1 || i == 0 || j == 0) {
				if (m[i][j] != '#') {
					esc.push_back({ i,j });
				}
			}
		}
	}
	bfs();

	bfs2(pos.first, pos.second);

	for (auto a : esc) {
		if (visited2[a.first][a.second] < visited[a.first][a.second]) {
			ret.push_back(visited2[a.first][a.second] + 1);
		}
	}
	if (ret.size() == 0) {
		cout << "IMPOSSIBLE";
	}
	else {
		sort(ret.begin(), ret.end());
		cout << ret.front();
	}

	//cout << '\n';
	//for (int i = 0; i < r; i++) {
	//	for (int j = 0; j < c; j++) {
	//		cout << visited[i][j] << ' ';
	//	}
	//	cout << '\n';
	//}

	//for (int i = 0; i < r; i++) {
	//	for (int j = 0; j < c; j++) {
	//		cout << visited2[i][j] << ' ';
	//	}
	//	cout << '\n';
	//}

	return 0;
}

0

qnvr31p

해결했습니다.

3 3

..F

.J#

.#.

이런 경우에서 [2][2]에 해당하는 자리가

visited2에서 -1(기본값 fill)로 초기화 된 후 방문 되지 않기 때문에

if (visited2[a.first][a.second] < visited[a.first][a.second]) (visited2 = 지훈, visited = 불)

이 조건문에 무조건 성립되어 ret 벡터에 -1에서 1을 더한 0 값이 들어가서

ret 벡터를 sort 후 답(벡터의 front())을 출력하면 0이 출력되는 문제가 있었습니다.

 

많은 답변 정말 감사합니다!!!

0

큰돌

해결하셨다니 다행이네요.. ㅎㅎ

 

도움이 되어드리지 못한 것 같아 죄송한 마음 뿐입니다..

감사합니다.

4 - A

0

21

2

코딩살구클럽 입장이 안됩니다

0

55

2

4-F 경우의 수 질문입니다.

0

32

2

코딩살구클럽 가입이 안됩니다.

0

66

2

살구 클럽에 대한 질문있습ㄴ디ㅏ

0

54

1

교안 158페이지 문의드립니다

0

44

2

코딩살구클럽 관련 건의사항

0

110

1

코살에 19942 다이어트 문제에 N의 범위가 빠져있슴니다

0

44

1

진행 방법 질문드립니다!

0

79

2

2-I) 왜 이 문제가 그래프이론 카테고리에 있는지 잘 모르겠습니다.

0

63

2

2주차 개념#12 트리 순회

0

32

2

백준사이트가 종료된다고 합니다.

0

313

2

백준 서비스 종료

9

945

1

sk 하이닉스 코테 대비

0

384

2

3-G 최댓값 질문

0

54

1

모듈러 연산 값이 10이 아닌 경우도 있지 않나요?

0

84

2

3-I 코드 질문드립니다.

0

63

2

3-N 질문 있습니다.

0

68

2

학습방법

0

105

2

4-H 질문 있습니다 (코드 리뷰)

0

69

2

코딩테스트 어디까지 준비해야 하는지 질문이 있습니다.

0

182

2

2-O 반례가 무엇일지 어떤 부분이 틀렸는지 잘 모르겠습니다.

0

71

2

2주차 개념 #4-2. 인접행렬 질문있습니다.

0

65

2

1-A 문제풀이 후 궁금한 점이 생겨서 질문드립니다.

0

53

2