거의 알고리즘 일기장

알고스팟_ 게임판 덮기 본문

알고리즘 문제풀이

알고스팟_ 게임판 덮기

건우권 2020. 4. 21. 15:38

https://www.algospot.com/judge/problem/read/BOARDCOVER

 

algospot.com :: BOARDCOVER

게임판 덮기 문제 정보 문제 H*W 크기의 게임판이 있습니다. 게임판은 검은 칸과 흰 칸으로 구성된 격자 모양을 하고 있는데 이 중 모든 흰 칸을 3칸짜리 L자 모양의 블록으로 덮고 싶습니다. 이 때 블록들은 자유롭게 회전해서 놓을 수 있지만, 서로 겹치거나, 검은 칸을 덮거나, 게임판 밖으로 나가서는 안 됩니다. 위 그림은 한 게임판과 이를 덮는 방법을 보여줍니다. 게임판이 주어질 때 이를 덮는 방법의 수를 계산하는 프로그램을 작성하세요. 입력 력의 첫

www.algospot.com

 풀이방법

 첫번째 풀이

 

 그냥 그 점에 대한 블록이 가질수 있는 12가지 형태를 모두 선택하는 방법을 사용했었다.

 

12가지 경우

 하지만 이렇게 풀게되면 시간도 ( 12^(50/3) = 184,884,258,895,036,416‬ ) x (테스트케이스 수) 말도 안되는데다가 중복으로 세는 경우도 있어 더 보완할 필요성이 있다.

 

 

 두번째 풀이

 

 생각해보면 항상 맨위쪽에서 맨 좌측의 경우의 점을 골라 블록이 대응하는지 살펴볼텐데, 이를 이용하면

 가능한 경우는 12에서 4가지로 줄어든다. ( 조금만 생각해보면 당연하다 )

 그러면 시간은 (4 ^ (50/3) ) x(테스트 케이스 수) = 128,849,018,880 이렇게 되는데  여전히 크긴하다.. ㅋㅋㅋ

 하지만, 저 네가지 경우가 다 배치될 확률은 거의 없고 한 1 ~2가지 정도 배치될테니 약 (2^ (50/3)) x(테스트 케이스 수) = 1,966,080‬ 이므로 무난히 통과 가능할것이다.

 그러니 시간제한인 2초안에는 무조건 들어갈거라고 생각할수있다.


전체코드

#include <iostream>
#include <algorithm>
#include <vector>

using namespace std;

vector<vector<int>>dy = { {0, 0, 1}, {0, 1, 1},{0, 1, 1},{0, 0, 1}};
vector<vector<int>>dx = { {0, 1, 1}, {0, 0, -1},{0, 0, 1},{0, 1, 0}};

vector<vector<char>> originalMap;
int N, M;
int emptyPlace;

void Input()
{
	cin >> N >> M;
	originalMap.resize(N+2);
	for (int i = 0; i < N; i++)
		originalMap[i].resize(M + 2);

	emptyPlace = 0;
	char value;
	for (int i = 0; i < N; i++)
	{
		for (int j = 0; j < M; j++)
		{
			cin >> value;
			originalMap[i][j] = value;
			if (value == '.')
				emptyPlace++;
		}
	}
}

bool inRange(int y, int x)
{
	if (y >= 0 && x >= 0 && y < N && x < M)
		return true;
	return false;
}

pair<int, int>findNextPoint(const vector<vector<char>>& map)
{
	for (int i = 0; i < N; i++)
		for (int j = 0; j < M; j++)
			if (map[i][j] == '.')
				return { i, j };
	return { -1, -1 };
}

int dfs(int y, int x, int dir, int cnt, vector<vector<char>>map)
{
	//기저 사례 : 남은 공간을 블록의 넓이로 나눴을때 나누어떨어지지 않으면 당연히 채울수 없음은 자명함.
	if (emptyPlace % 3 != 0)
		return 0;

	//채우기
	for (int i = 0; i < 3; i++)
	{
		int ny = y + dy[dir][i];
		int nx = x + dx[dir][i];

		//범위가 넘었으면 return 0
		if (inRange(ny, nx) == false)
			return 0;

		//이미 자리가 있으면 return 0
		if (map[ny][nx] != '.')
			return 0;

		map[ny][nx] = '#';
	}

	//탈출조건 : cnt가 이때까지 문제없이 왔다면 전체 공간을 다 채웠음은 당연함.
	if ((emptyPlace / 3) == cnt)
		return 1;

	int ret = 0;

	//다음거 물색
	pair<int, int> nextPos = findNextPoint(map);
	for (int i = 0; i < 4; i++)
	{
		ret += dfs(nextPos.first, nextPos.second, i, cnt+1, map);
	}
	return ret;
}

int main()
{
	int t;
	cin >> t;
	for (int i = 0; i < t; i++)
	{
		Input();
		auto firstPos = findNextPoint(originalMap);
		int ans = 0;
		for(int i =0; i<4; i++)
			ans += dfs(firstPos.first, firstPos.second,i, 1, originalMap);
		cout << ans << endl;
	}

	return 0;
}

 후기

 종만북 코드가 더 깔끔하네..

 

 종만북처럼 맵을 복사하는게 아니고 블록을 넣었다 뺐다 하는식으로 짜면 훨씬 좋을것같다.

반응형

'알고리즘 문제풀이' 카테고리의 다른 글

알고스팟 _ 시계 맞추기  (0) 2020.04.22
알고스팟_여행하는 외판원  (5) 2020.04.22
알고스팟__소풍  (0) 2020.04.20
백준 3190번 _ 뱀  (0) 2020.04.19
백준 12100번 _2048(Easy)  (0) 2020.04.19
Comments