(C++) 백준 14500번 - 테트로미노

코딩너구리·2026년 1월 5일

코딩 문제 풀이

목록 보기
147/266

https://www.acmicpc.net/problem/14500

문제

> 폴리오미노란 크기가 1×1인 정사각형을 여러 개 이어서 붙인 도형이며, 다음과 같은 조건을 만족해야 한다.

1. 정사각형은 서로 겹치면 안 된다.
2. 도형은 모두 연결되어 있어야 한다.
3. 정사각형의 변끼리 연결되어 있어야 한다. 즉, 꼭짓점과 꼭짓점만 맞닿아 있으면 안 된다.

> 정사각형 4개를 이어 붙인 폴리오미노는 테트로미노라고 하며, 다음과 같은 5가지가 있다.

> 아름이는 크기가 N×M인 종이 위에 테트로미노 하나를 놓으려고 한다. 
> 종이는 1×1 크기의 칸으로 나누어져 있으며, 각각의 칸에는 정수가 하나 쓰여 있다.
> 테트로미노 하나를 적절히 놓아서 테트로미노가 놓인 칸에 쓰여 있는 수들의 합을 최대로 하는 프로그램을 작성하시오.
> 테트로미노는 반드시 한 정사각형이 정확히 하나의 칸을 포함하도록 놓아야 하며, 회전이나 대칭을 시켜도 된다.

접근

브루트포스와 DFS를 통해 주어진 각각의 좌표에서 가능한 모든 경우를 따져준다.
추가로 ㅗ,ㅜ,ㅓ,ㅏ모양은 DFS로 중간에 꺾일 수 없기 때문에 따로 빼서 검사한다.
총 4개의 블럭을 쓰기 때문에 DFS에서 재귀로 사방탐색을 하여 가능한 모든 경로를 따진다. 그러면 가능한 테트로미노의 회전, 대칭이 모두 따져진다.
따지면서 더한 결과를 서로 비교해서 최대를 찾아주면된다.

문제해결

> 테트로미노를 탐색하는 DFS를 만든다. 입력으로 행,열이 들어오고, 몇개의 정사각형이 이어졌는지를 나타내기위해 num을, 각 폴리오미노의 값을 더한 sum을 받는다.
> 테트로미노의 조건으로 4개의 정사각형이 만들어졌을 때를 위해 num이 4인지 검증하고 맞다면 현재 위치까지의 값을 다 더해 그동안 누적된 Max값과 비교해 갱신해준다.
> 4개가 아니라면 더 이어줘야하므로 사방탐색으로 다음 좌표인 nr,nc를 구해 범위 밖인지 검증하고 범위 내라면 재귀로 해당 좌표를 가지고 들어간다.
> 재귀로 들어가기전 방문처리를 해주고, 재귀가 4개를만족하던, 불가능한경로여서 깨지던 다시 돌아왔을 때, 방문처리를 다시 false로 해줘야 모든 경우를 따져줄 수 있다.
> 이제 DFS로 못하는 T모양을 따져준다. 입력으로 현 좌표의 행과 열을 가지고, 메소드 내부에서 이를 기준으로 ㅗ,ㅜ,ㅓ,ㅏ 모양을 봐야하므로 각각 상하좌우의 좌표를 구해준다.
> 조건을 나누어 모양별로 각각의 상하좌우 좌표가 범위내에 있다면 따져주어야 하므로 이를 검증하고 값을 구해 각각 Max와 비교해 최대값을 갱신해준다.
> 이제 main함수에서 폴리오미노판을 입력받고 모든 좌표를 DFS에 넣어 따져준다. DFS가 끝나고 나오면 그 좌표의 T모양도 따져야 하므로 ShapeT메소드도 호출해 구해준다.
> 최종적으로 Max에 있는 최대값을 출력한다.

코드

#include <iostream>
#include <algorithm>
#include <vector>
using namespace std;

int N, M;
vector<vector<int>> paper;
vector<vector<bool>> visited;
int Max = 0;
int dir[4] = { -1, 1, 0, 0 };
int dic[4] = { 0, 0, -1, 1 };
void DFS(int r, int c, int num, int sum)
{
	if (num == 4)
	{
		sum += paper[r][c];
		Max = max(Max, sum);
		return;
	}

	for (int i = 0; i < 4; i++)
	{
		int nr = r + dir[i];
		int nc = c + dic[i];

		if (nr < 0 || nr >= N) continue;
		if (nc < 0 || nc >= M) continue;

		if (!visited[nr][nc])
		{
			visited[nr][nc] = true;
			DFS(nr, nc, num + 1, sum + paper[r][c]);
			visited[nr][nc] = false;
		}
	}
}
void ShapeT(int r, int c)
{
	int upnr = r - 1;
	int upnc = c;

	int lnr = r;
	int lnc = c - 1;

	int rnr = r;
	int rnc = c + 1;
	
	int dnr = r + 1;
	int dnc = c;

	if (upnr >= 0 && lnc >= 0 && rnc < M)//ㅗ
	{
		int upT = paper[r][c] + paper[upnr][upnc] + paper[lnr][lnc] + paper[rnr][rnc];
		Max = max(upT, Max);
	}

	if (upnr >= 0 && lnc >= 0 && dnr < N)//ㅓ
	{
		int leftT = paper[r][c] + paper[upnr][upnc] + paper[lnr][lnc] + paper[dnr][dnc];
		Max = max(leftT, Max);
	}

	if (upnr >= 0 && rnc < M && dnr < N)//ㅏ
	{
		int rightT = paper[r][c] + paper[upnr][upnc] + paper[dnr][dnc] + paper[rnr][rnc];
		Max = max(rightT, Max);
	}

	if (dnr < N && lnc >= 0 && rnc < M)//ㅜ
	{
		int downT = paper[r][c] + paper[dnr][dnc] + paper[lnr][lnc] + paper[rnr][rnc];
		Max = max(downT, Max);
	}
}
int main()
{
	ios::sync_with_stdio(false);
	cin.tie(nullptr);
	cout.tie(nullptr);

	cin >> N >> M;
	paper.resize(N, vector<int>(M));
	visited.assign(N, vector<bool>(M, false));
	for (int i = 0; i < N; i++)
	{
		for (int j = 0; j < M; j++)
		{
			cin >> paper[i][j];
		}
	}

	for (int i = 0; i < N; i++)
	{
		for (int j = 0; j < M; j++)
		{
			visited[i][j] = true;
			DFS(i, j, 1, 0);
			visited[i][j] = false;
			ShapeT(i, j);
		}
	}
	cout << Max << '\n';
}

후기

각 테트로미노에 대해 4개의 사각형중 어떤 사각형을 기준으로 잡을지, 가능한 경우를 전부 따져야 하는지 고민이 많았지만 어딜 기준으로 잡던 가능한 경우를 전부 따지다 보면 다 고려가 되는걸 알았다. 그래서 일단 다 따져보니 됐다.

0개의 댓글