ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • JUNGOL...197
    일지 2022. 9. 10. 21:41

    Intermediate_Coder/그래프탐색-BFS/화염에서탈출(SLIKAR)


    문제                                            

    재우는 중간계(middle-earth)에서 유명한 용사이다.

     

    어느날 사람들에게 부탁 받은 마물 퇴치를 신속히 해결하고 집으로 돌아가려고 하는데, 

    마법의 숲에서 재우를 추적하고 있던 불의 마법사 무길이와 마주치게 되었다. 

    무길이는 재우를 잡기 위해서 화염 마법을 시전하였고 어느덧 숲은 화염으로 가득차고 있었다. 

    재우는 무길이가 화염을 풀어 놓은 숲에서 신속히 요새로 귀환하고자 한다!

     

    숲의 지도는 R행과 C열로 이루어져있다. 

    비어있는 칸은 '.'로 표시되고, 불은 '*'로, 바위는 'X'로 표시되어있다. 

    용사의 집은 'D'로 표현되고, 재우가 처음에 서있는 위치는 'S'로 표시된다.

     

    매 분마다 재우는 인접한 4개의 칸(상, 하, 좌, 우)으로 이동할 수 있다. 

    불은 매 분마다 인접한 4개의 칸에 불을 옮긴다.

    재우는 불과 바위를 지나지 못한다. 

    불은 바위와 요새에 옮겨지지 않는다.

     

    재우가 탈출을 할 수 있을 때 몇 분 만에 탈출 할 수 있는지 알아보는 프로그램을 작성하라.

     

    입력 형식                                     

    입력의 첫번째 줄에는 50이하의 정수인 R과 C가 입력된다.

    다음 줄부터 지도가 입력되며, 이는 R개의 줄로 이루어져있다. 

    각 R개의 줄에는 C개의 문자가 입력된다. 

    지도에는 정확히 하나의 용사의 집과 하나의 시작 위치가 입력된다.

     

    출력 형식                                     

    재우가 숲에서 용사의 집으로 돌아올 수 있을 때 최단 시간(분)을 출력하고,

    만약 탈출이 불가능할 경우 "impossible"을 출력한다.

     

    입력 예                                        

    3 3       | 3 3               | 3 6          | 8 6
    D.*       | D.*               | D...*.       | D...*.
    ...         | ...                 | .X.X..      | .XXX..
    .S.        | ..S               | ....S.        | .X*...

                                                       | .XS...

                                                       | .XXX..

                                                       | ......

                                                       | XXXXX.

                                                       | *.....

     

    출력 예                                        

    3          | impossible   | 6             | 13


    EscapeFromFire.h

    #pragma once
    #include <iostream>
    #include <queue>
    #include <vector>
    
    #include "../../Base.h"
    
    using std::queue;
    using std::vector;
    
    class EscapeFromFire : public Base
    {
    private:
    	struct Point
    	{
    		int x, y;
    		int minute;
    
    		Point() : x(0), y(0), minute(0) {}
    		Point(int x, int y, int day = 0) : x(x), y(y), minute(day) {}
    	};
    
    	void SimulateFire();
    	int CalculateEscapeMinutes();
    
    	bool PossibleToFire(int x, int y);
    	bool PossibleToGo(int x, int y, int curMinute);
    
    	bool IsInMap(int x, int y);
    	bool IsArrive(int x, int y);
    
    protected:
    	void Code() override;
    
    private:
    	const int xDir[4]{ 0, 0, -1, 1 };
    	const int yDir[4]{ -1, 1, 0, 0 };
    
    	const int EMPTY{ 999'999'999 };
    	const int BLOCK{ -1 };
    
    	int r{}, c{};
    	char map[50][50]{};
    	int fireSimulatedMap[50][50]{};
    
    	queue<Point> q;
    	Point startPos;
    	Point endPos;
    };

     

    EscapeFromFire.cpp

    void EscapeFromFire::Code()
    {
    	std::cin >> r >> c;
    
    	for (int i = 0; i < r; i++)
    	{
    		for (int j = 0; j < c; j++)
    		{
    			std::cin >> map[i][j];
    
    			fireSimulatedMap[i][j] = EMPTY;
    
    			if (map[i][j] == 'S')
    			{
    				startPos = { j, i };
    			}
    
    			if (map[i][j] == 'D')
    			{
    				endPos = { j, i };
    				fireSimulatedMap[i][j] = BLOCK;
    			}
    
    			if (map[i][j] == 'X')
    			{
    				fireSimulatedMap[i][j] = BLOCK;
    			}
    
    			if (map[i][j] == '*')
    			{
    				fireSimulatedMap[i][j] = 0;
    				q.push({ j, i });
    			}
    		}
    	}
    
    	SimulateFire();
    
    	int minutes{ CalculateEscapeMinutes() };
    	if (minutes < 0)
    		std::cout << "impossible";
    	else
    		std::cout << minutes;
    }
    
    /// <summary>
    /// 불이 번지는 과정을 저장한다.
    /// </summary>
    void EscapeFromFire::SimulateFire()
    {
    	while (!q.empty())
    	{
    		Point p{ q.front() };
    		q.pop();
    
    		for (int i = 0; i < 4; i++)
    		{
    			int nextX{ p.x + xDir[i] }, nextY{ p.y + yDir[i] };
    			if (IsInMap(nextX, nextY) && PossibleToFire(nextX, nextY))
    			{
    				int nextMinute{ p.minute + 1 };
    
    				fireSimulatedMap[nextY][nextX] = nextMinute;
    
    				q.push({ nextX, nextY, nextMinute });
    			}
    		}
    	}
    }
    
    /// <summary>
    /// 탈출하는 데 걸리는 시간을 반환한다.
    /// </summary>
    /// <returns>탈출한 시간(불가능한 경우 -1)</returns>
    int EscapeFromFire::CalculateEscapeMinutes()
    {
    	q.push(startPos);
    	while (!q.empty())
    	{
    		Point p{ q.front() };
    		q.pop();
    
    		for (int i = 0; i < 4; i++)
    		{
    			int nextX{ p.x + xDir[i] }, nextY{ p.y + yDir[i] };
    			int nextMinute{ p.minute + 1 };
    
    			if (IsInMap(nextX, nextY) && PossibleToGo(nextX, nextY, nextMinute))
    			{
    				if (IsArrive(nextX, nextY))
    				{
    					return nextMinute;
    				}
    
    				q.push({ nextX, nextY, nextMinute });
    			}
    		}
    
    		map[p.y][p.x] = 'X';
    	}
    
    	return -1;
    }
    
    /// <summary>
    /// 불 태울 수 있는지 여부를 반환한다.
    /// </summary>
    /// <param name="x">x 좌표</param>
    /// <param name="y">y 좌표</param>
    /// <returns>태울 수 있는지 여부</returns>
    bool EscapeFromFire::PossibleToFire(int x, int y)
    {
    	return fireSimulatedMap[y][x] == EMPTY;
    }
    
    /// <summary>
    /// 이동 가능한지 여부를 반환한다.
    /// </summary>
    /// <param name="x">x 좌표</param>
    /// <param name="y">y 좌표</param>
    /// <param name="curMinute">진행 시간</param>
    /// <returns>이동 가능 여부</returns>
    bool EscapeFromFire::PossibleToGo(int x, int y, int curMinute)
    {
    	bool isOnFire{ fireSimulatedMap[y][x] != BLOCK && curMinute >= fireSimulatedMap[y][x] };
    	bool isOnRock{ map[y][x] == 'X' };
    	return !isOnFire && !isOnRock;
    }
    
    /// <summary>
    /// 맵 범위에 벗어나는지 여부를 반환한다.
    /// </summary>
    /// <param name="x">x 좌표</param>
    /// <param name="y">y 좌표</param>
    /// <returns>맵을 벗어나는지 여부</returns>
    bool EscapeFromFire::IsInMap(int x, int y)
    {
    	bool xInMap{ 0 <= x && x < c };
    	bool yInMap{ 0 <= y && y < r };
    	return xInMap && yInMap;
    }
    
    /// <summary>
    /// 도착했는지 여부를 반환한다.
    /// </summary>
    /// <param name="x">x 좌표</param>
    /// <param name="y">y 좌표</param>
    /// <returns>도착 여부</returns>
    bool EscapeFromFire::IsArrive(int x, int y)
    {
    	return x == endPos.x && y == endPos.y;
    }

     


    실행 결과 Runtime Error(60)

    원인 전체적인 리펙토링을 진행해서 분단위로 맵 전체를 저장하는 대신 하나의 맵에 불타는 시간을 저장하는 식으로 처리했으나 여전히 Bad_Alloc 이 발생하고 있다. 어디선가 범위를 넘어서는 값을 접근하는 것 같은데 파악일 되질 않는다.

    처리 방법 다른 블로그의 코드를 참고해서 어떤 부분에서 범위를 초과할 수 있는지 확인해봐야 할 것 같다.


     

    NadanKim/CodingTest_JUNGOL: JUNGOL 코딩 테스트를 위한 저장소 (github.com)

     

    NadanKim/CodingTest_JUNGOL

    JUNGOL 코딩 테스트를 위한 저장소. Contribute to NadanKim/CodingTest_JUNGOL development by creating an account on GitHub.

    github.com

     

    댓글

Designed by Tistory.