인프런 커뮤니티 질문&답변

shhmun02님의 프로필 이미지
shhmun02

작성한 질문수

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

3-K와 문제의 단순화

3-k 시간초과 질문 있습니다.

해결된 질문

작성

·

236

0

http://boj.kr/abd4a896e8f8492ebcc55792822142f0
먼저 입력을 받으며 ice라는 큐에 얼음의 위치를 전부 담고
bfs_swan으로 백조가 서로 만날 수 있는 지 확인합니다.
그 후에 melt_ice라는 함수로 ice에 들어있는 큐를 이용해서 얼음을 녹이며 녹지 않은 얼음을 ice에 담고 check_swan 함수를 이용해서 한쪽 백조의 위치 주변에 녹은 얼음을 시작점으로 하여 다른쪽의 백조에 닿을 수 있는 지 bfs로 탐색합니다.(이 때 백조의 visited배열은 초기화를 해주지 않습니다.) 이렇게 melt_ice, check_swan 을 계속 반복해주며 로직을 반복하는데 어느 부분에서 시간초과가 나는 지 잘 모르겠습니다. 감사합니다!!

답변 2

0

큰돌님의 프로필 이미지
큰돌
지식공유자

안녕하세요 02님 ㅎㅎ

시간초과가 일어나는 부분, 불필요한 부분은 다음과 같습니다.

void bfs_swan(int y, int x)
{
	int ny, nx;

매번 초기 시작점에서부터 BFS가 불필요하게 일어납니다.

melt_ice : 괜찮습니다.

check_swan : 괜찮습니다.

 

감사합니다.

shhmun02님의 프로필 이미지
shhmun02
질문자

답변 감사합니다 ㅎㅎ
매번 초기 시작점에서부터 BFS가 불필요하게 일어납니다 <- 이 부분이 잘 이해가 안가서 그러는데요. 저는 while 문의 melt_ice, check_swan을 돌기전에 먼저 bfs_swan을 이용해서 백조가 다른 백조를 만날 수 있는지 확인을 하고 while문을 들어가는 로직으로 코드를 작성했는데 처음에 이 확인 작업이 잘못 되었다는 뜻인가요??

큰돌님의 프로필 이미지
큰돌
지식공유자

안녕하세요 02님 ㅎㅎ

아 제가 착각을 했는데요. 저게 매번 일어나는 거 같았는데 매번 안 일어나네요.

다만 불필요한 부분은 다음과 같습니다.

#include <bits/stdc++.h>
using namespace std;

int arr[1504][1504], visited[1504][1504];
int dy[] = {-1, 0, 1, 0};
int dx[] = {0, 1, 0, -1};
queue<pair<int, int>>ice;
queue<pair<int, int>>position;
vector<pair<int, int>>swan;
string str;
int r, c, cnt, sw;

void melt_ice()
{
	int x, y, nx, ny, qsize, flag;
	qsize = ice.size();
	queue<pair<int, int>>q;
	for (int i = 0; i < qsize; i++)
	{
		tie(y, x) = ice.front();
		ice.pop();
		flag = 0;
		for (int i = 0; i < 4; i++)
		{
			ny = y + dy[i];
			nx = x + dx[i];
			if (ny >= r || ny < 0 || nx >= c || nx < 0) continue;
			if (arr[ny][nx] == 0)
			{
				flag = 1;
				q.push({y, x});
				visited[y][x] = 0;
				break ;
			}
		}
		if (flag == 0)
			ice.push({y, x});
	}
	while (q.size())
	{
		tie(y, x) = q.front();
		q.pop();
		arr[y][x] = 0;
	}
}

void bfs_swan(int y, int x)
{
	int ny, nx;
	queue<pair<int, int>> q;
	visited[y][x] = 1;
	q.push({y, x});
	while(q.size())
	{
		tie(y, x) = q.front();
		q.pop();
		for (int i = 0; i < 4; i++)
		{
			ny = y + dy[i];
			nx = x + dx[i];
			if (ny >= r || ny < 0 || nx >= c || nx < 0 || visited[ny][nx]) continue;
			visited[ny][nx] = 1;
			if (arr[ny][nx] == -1)
			{
				position.push({ny, nx});
				continue;
			}
			q.push({ny, nx});
			if (visited[swan[1].first][swan[1].second])
			{
				sw = 1;
				return;
			}
		}
	}
}

void check_swan()
{
	int x, y, nx, ny, psize, idx;
	queue<pair<int, int>> q;
	psize = position.size();
	idx = 0;
	while (idx < psize)
	{
		tie(y, x) = position.front();
		position.pop();
		q.push({y, x});
		visited[y][x] = 1;
		while (q.size())
		{
			tie(y, x) = q.front();
			q.pop();
			for (int i = 0; i < 4; i++)
			{
				ny = y + dy[i];
				nx = x + dx[i];
				if (ny >= r || ny < 0 || nx >= c || nx < 0 || visited[ny][nx]) continue;
				visited[ny][nx] = 1;
				if (arr[ny][nx] == -1)
				{
					position.push({ny, nx});
					continue;
				}
				q.push({ny, nx});
				if (visited[swan[1].first][swan[1].second])
				{
					sw = 1;
					return;
				}
			}
		}
		idx++;
	}
}

int main(void)
{
	ios::sync_with_stdio(false);
	cin.tie(NULL); cout.tie(NULL);
	cin >> r >> c;
	for (int i = 0; i < r; i++)
	{
		cin >> str;
		for (int j = 0; j < str.length(); j++)
		{
			if (str[j] == '.')
			{
				arr[i][j] = 0;
			}
			else if (str[j] == 'X')
			{
				arr[i][j] = -1;
				ice.push({i, j});
			}
			else
			{
				arr[i][j] = 0;
				swan.push_back({i, j});
			}
		}
	}
	bfs_swan(swan[0].first, swan[0].second);
	for(int i = 0; i < r; i++){
		for(int j = 0; j < c; j++){
			cout << visited[i][j];
		}
		cout <<'\n';
	}
//	while (ice.size() && sw == 0)
//	{
//		melt_ice();
//		cnt++;
//		check_swan();
//	}
	cout << cnt;
}

이렇게 한번 해볼까요?

이렇게 하고 ex3을 입력하면

00000000000000000
00000000000000000
00000001100000000
00000011110000000
00000011110000000
00000011111000000
00000011111000000
00000001111000000

이런식으로 나옵니다.

이 때 만들어진 position을 기반으로 check_swan이 동작합니다.

이 함수는 똑같은 로직이죠.

void check_swan()
{
	int x, y, nx, ny, psize, idx;
	queue<pair<int, int>> q;
	psize = position.size();

즉, 똑같은게 2번 반복됩니다.

 

또한, ice를 넣고

			else if (str[j] == 'X')
			{
				arr[i][j] = -1;
				ice.push({i, j});
			}

 

이런식으로 네방향을 기반으로 바깥쪽의 얼음을 찾은 아이디어는 좋으나 비효율적입니다.

		for (int i = 0; i < 4; i++)
		{
			ny = y + dy[i];
			nx = x + dx[i];
			if (ny >= r || ny < 0 || nx >= c || nx < 0) continue;
			if (arr[ny][nx] == 0)
			{
				flag = 1;
				q.push({y, x});
				visited[y][x] = 0;
				break ;
			}

 

바깥부분부터 시작해 탐색을 이어가면 바깥 얼음이라는 것은 자명하니 이부분은 신경쓸필요가 없게 됩니다.

 

또 질문 있으시면 언제든지 질문 부탁드립니다.

좋은 수강평과 별점 5점은 제게 큰 힘이 됩니다. :)

감사합니다.

강사 큰돌 올림.

shhmun02님의 프로필 이미지
shhmun02
질문자

감사합니다!!

0

안녕하세요, 인프런 AI 인턴이에요.

주어진 코드에서 시간 초과가 발생하는 이유는 다양한 경우가 있을 수 있습니다. 하지만 코드를 보면서 몇 가지 개선해볼 점을 제시해드릴게요.

먼저, 주어진 문제에서 시간 초과가 발생한다면 가장 큰 원인은 탐색의 중복입니다. 따라서 중복된 탐색을 피하기 위해 이미 탐색한 위치를 기록하여 해당 위치를 다시 방문하지 않도록 하는 것이 좋습니다.

또한, 큐를 사용할 때 큐에 넣는 방식에 따라 성능이 달라질 수 있습니다. 현재 코드에서는 큐에 원소를 삽입하는 동안 큐의 크기만큼 반복문을 실행하고 있습니다. 이 경우 큐의 크기가 클수록 반복 횟수도 많아지므로 성능 저하의 원인이 될 수 있습니다. 이를 개선하기 위해서는 큐에 삽입할 때 바로 확인하는 방식으로 수정하는 것이 좋습니다.

그리고 큐에 넣는 위치를 단순히 모든 인덱스에 대해 확인하는 것이 아니라, 녹는 얼음의 위치 주변만 확인하도록 수정하는 것도 좋은 방법입니다. 이렇게 하면 불필요한 탐색을 줄일 수 있습니다.

마지막으로, BFS 탐색 시에는 방문한 위치를 체크하는 visited 배열을 사용하는 것이 좋습니다. 코드에서는 visited 배열을 초기화하지 않고 사용하고 있는 것 같아요. 따라서 탐색할 때마다 visited 배열을 초기화해주어야 합니다.

위의 점들을 참고하여 코드를 수정하고 실행해보세요. 그럼 좋은 결과를 얻을 수 있을 것입니다. 질문이 더 있으시면 언제든지 물어보세요. 좋은 결과 있길 바라요!

shhmun02님의 프로필 이미지
shhmun02

작성한 질문수

질문하기