본문 바로가기

CodingTest Exam/[C++] Algorithm Study

90. 라이언 킹 심바(삼성 SW역량평가 기출 : BFS 활용) (it 취업을 위한 알고리즘 문제풀이 입문 (with C/C++) : 코딩테스트 대비) ★★★★★

 

#include <stdio.h>
#include <queue>

using namespace std;

int a[26][26];
int ch[26][26];

struct Pos
{
	int x;
	int y;
	int dis;
	
	Pos(int x, int y, int z)
	{
		this -> x = x;
		this -> y = y;
		this -> dis = z;
	}
	
	bool operator<(const Pos &a) const
	{
		if(dis == a.dis)
		{
			if(x == a.x) return y > a.y;
			else
				return x > a.x;
		}
		else return dis > a.dis;
	}
};

struct Simba
{
	int x;
	int y;
	int size;
	int cnt;
	
	void SizeUp()
	{
		cnt = 0;
		size++;
	}
};

int main()
{
	int n, res = 0;
	scanf("%d", &n);
	
	int dx[4] = {-1, 0, 1, 0};
	int dy[4] = {0, 1, 0, -1};
	
	priority_queue<Pos> pQ;
	
	Simba simba;
	
	for(int i = 1; i <= n; i++)
	{
		for(int j = 1; j <= n; j++)
		{
			scanf("%d", &a[i][j]);
			if(a[i][j] == 9)
			{
				simba.x = i;
				simba.y = j;
				a[i][j] = 0;
			}
		}
	}
	
	pQ.push(Pos(simba.x, simba.y, 0));
	simba.size = 2;
	simba.cnt = 0;
	
	while(!pQ.empty())
	{
		Pos temp = pQ.top();
		pQ.pop();
		
		int x = temp.x;
		int y = temp.y;
		int dis = temp.dis;
		
		if(a[x][y] != 0 && a[x][y] < simba.size)
		{
			a[x][y] = 0;	
			simba.x = x;
			simba.y = y;
			simba.cnt++;
			
			if(simba.cnt == simba.size)
				simba.SizeUp();
				
			for(int i = 1; i <= n; i++)
			{
				for(int j = 1; j <= n; j++)
				{
					ch[i][j] = 0;
				}
			}
			
			while(!pQ.empty()) pQ.pop();
			res = dis;
		}
		
		for(int i = 0; i < 4; i++)
		{
			if(x+dx[i] < 1 || x+dx[i] > n || y+dy[i] < 1 || y+dy[i] > n || a[x+dx[i]][y+dy[i]] > simba.size || ch[x+dx[i]][y+dy[i]] > 0) continue;
			
			ch[x+dx[i]][y+dy[i]] = 1;
			pQ.push(Pos(x+dx[i], y+dy[i], dis+1));
		}
	}
	
	printf("%d", res);
	
	return 0;
}

1. 토끼의 위치를 우선순위큐에 저장해서 가장 가까우면서, 가장 위쪽이면서, 가장 왼쪽인 토끼를 잡아먹을 수 있게 심바를 이동시켜야한다. 이는 아래와 같이 표현할 수 있다.

struct Pos
{
	int x;
	int y;
	int dis;
	
	Pos(int x, int y, int z)
	{
		this -> x = x;
		this -> y = y;
		this -> dis = z;
	}
	
	bool operator<(const Pos &a) const
	{
		if(dis == a.dis)
		{
			if(x == a.x) return y > a.y;
			else
				return x > a.x;
		}
		else return dis > a.dis;
	}
};

연산자 오버로딩을 통해서 우선순위큐로 들어가면 위와 같이 표현할시에 최소힙으로 들어간다.

if (비교 대상 거리 == 입력 대상 거리) 가 이라면

 

if (비교 대상 행의 위치 == 입력 대상 행의 위치) 가 이라면 입력 대상 열의 위치비교 대상 열의 위치보다 작게

거짓이라면 입력 대상 행의 위치비교 대상 행의 위치보다 작게

 

if (비교 대상 거리 == 입력 대상 거리) 가 거짓이라면 입력 대상 거리 비교 대상 거리보다 작게

 

이렇게 하면

2마리 이상이면 가장 가까운 토끼 먹기
 - 가장 위쪽 // 가장 왼쪽

조건을 만족할 수 있게 된다.

 

 

 

 

2. 심바의 위치, 크기, 먹은 토끼의 수를 Simba 라는 구조체의 멤버 변수로 지정하고 Simba 형태의 simba를 생성한다.

struct Simba
{
	int x;
	int y;
	int size;
	int cnt;
	
	void SizeUp()
	{
		cnt = 0;
		size++;
	}
};

int main()
{
	Simba simba;
}

이 때 생성자로 생성할 필요는 없다.

 

 

 

3. 입력이 9라면 심바의 위치로 생각하고 현재 심바의 위치를 i행 j열로 두고, 그 자리를 0으로 초기화해준다. 나중에 지나갈 수도 있는 길이기 때문이다. (9로 해두면 크기가 9인 토끼가 있는 것으로 간주되어버린다)

for(int i = 1; i <= n; i++)
	{
		for(int j = 1; j <= n; j++)
		{
			scanf("%d", &a[i][j]);
			if(a[i][j] == 9)
			{
				simba.x = i;
				simba.y = j;
				a[i][j] = 0;
			}
		}
	}

 

4. 먼저 우선순위큐에서 값을 빼온다. 그 값은 심바의 위치가 아닌 토끼의 위치로 간주한다. 그 이유는 6번에서 나온다. 그 값이 0이 아니고 심바보다 사이즈가 작다면 (사이즈가 0이 아닌 심바보다 작은 토끼) 그 토끼를 먹고 그 자리를 차지한다. 이후 심바가 이동했던 길을 체크하는 ch 배열을 모두 0으로 초기화해준다. 그 이유도 6번에서 나온다.

 

5. 결과값 res = dis 로 해주는데, 이는 지금까지 심바가 이동한 거리이다. 계속해서 Pos(x, y, z) 로 z를 통해서 넘겨주기 때문이다. 또한 우선순위큐를 모두 비워줘야하는데 이 이유도 6번에서 나온다.

		Pos temp = pQ.top();
		pQ.pop();
		
		int x = temp.x;
		int y = temp.y;
		int dis = temp.dis;
		
		if(a[x][y] != 0 && a[x][y] < simba.size)
		{
			a[x][y] = 0;	
			simba.x = x;
			simba.y = y;
			simba.cnt++;
			
			if(simba.cnt == simba.size)
				simba.SizeUp();
				
			for(int i = 1; i <= n; i++)
			{
				for(int j = 1; j <= n; j++)
				{
					ch[i][j] = 0;
				}
			}
			
			while(!pQ.empty()) pQ.pop();
			res = dis;
		}

 

6. 이유

토끼의 위치로 간주하는 이유 : 만약 첫 if 문 사이즈가 작은 토끼인지 아닌지의 조건을 만족하지 않는다면 아래 for문으로 들어오게 된다.

	for(int i = 0; i < 4; i++)
	{
		if(x+dx[i] < 1 || x+dx[i] > n || y+dy[i] < 1 || y+dy[i] > n || a[x+dx[i]][y+dy[i]] > simba.size || ch[x+dx[i]][y+dy[i]] > 0) continue;
		
		ch[x+dx[i]][y+dy[i]] = 1;
		pQ.push(Pos(x+dx[i], y+dy[i], dis+1));
	}

상, 하, 좌, 우 4방향을 모두 확인해서 n*n을 벗어나지 않고, 심바보다 사이즈가 작으며, 이미 이동한 길이 아니라면

이동한 길을 1로 체크하고 우선순위큐에 넣는다. 이 우선순위큐에 넣는 행위 때문에 토끼의 위치로 간주하는 것이다. 왜냐하면 다음 while에서 심바가 이동한 것으로 간주하기 때문이다.

 

- 토끼를 먹고나면 ch배열을 초기화하는 이유 : 위와 같이 토끼를 먹지 못했다면 가장 가까운 토끼까지 이동하기 위해 최단거리를 알기 위해서 ch배열을 쓴다. 근데 토끼를 먹었다? 그렇다면 다시 다음 최단거리의 토끼를 알아야하기 때문에 ch를 초기화해주어야한다.

 

- 우선순위큐를 모두 비워주는 이유 : 위 코드에서는 for 문을 i가 3이 될 때까지 돈다. 즉 상, 하, 좌, 우 4방향 중에 조건을 만족하는 모든 방향이 우선순위큐에 들어가게 된다. 그 중에서 가장 가깝고, 위쪽이면서, 왼쪽인 것만을 찾아서 이동해야하기 때문에 그것을 제외한 우선순위큐에 있는 값은 전부 빼주어야한다.