백준 7576 - 토마토

2 minute read

문제링크

문제설명

  • N x M 크기의 창고가 있고, 1x1 크기의 상자들로 이루어져 있으며 각 상자에는 토마토를 보관한다.
  • 각 상자에는 빈 칸(0), 잘 익은 토마토(1), 아직 익지 않은 토마토(2) 가 있을 수 있다.
  • 보관 후 하루가 지나면 익은 토마토들의 상하좌우 인접한 곳에 있는 익지 않은 토마토들은 익은 토마토의 영향을 받아 익게 된다.
  • 창고에 보관된 토마토들이 며칠이 지나면 모두 익게 되는지 그 최소 일수를 구하라
  • 모든 토마토가 익을 수 없다면 -1 을 출력하라

문제접근

  • 시간초과로 풀지 못한 접근
    • 모든 익은 토마토의 좌표를 시작점으로 하여 창고 전체 영역으로 너비우선탐색을 한다.
    • 익는 동선이 겹치는 칸은 적게 소요된 날짜로 값을 갱신할 수 있도록 한다.
    • 최악의 경우 너비우선탐색을 백만번 반복해야 하고, 각각에 대해서 모든 정점을 방문해야 하고, 4가지 방향에 대해서 loop 를 형성하기 때문에 시간초과에 걸린다.
  • 토마토가 아닌 날짜를 중심으로 문제 접근
    • 0일차, 1일차, 2일차 … 에 익은 토마토들의 좌표를 담는 인접리스트 vector<pair<int, int>> days[] 를 선언한다.
    • n일차 익은 토마토를 시작으로 인접한 칸에 방문하여 다음날에 익게 되는 토마토를 n+1 일차 배열에 담는다.
    • 토마토가 하나도 익지 않게 되는 날까지 위 과정을 반복한다.

구현

#include <iostream>
#include <vector>
#include <queue>
#define MAX 1000000
using namespace std;

int N, M;
int box[1000][1000];
int dx[4] = { -1, 1, 0, 0 };
int dy[4] = { 0, 0, -1, 1 };
// n일차에 익은 토마토의 좌표
vector<pair<int, int>> days[MAX];

int spendDay(int cday) {
	int nx, ny, cnt = 0;
	for (auto ele : days[cday]) {
		for (int dir = 0; dir < 4; dir++) {
			nx = ele.first + dx[dir];
			ny = ele.second + dy[dir];
			if (nx < 0 || nx >= N || ny < 0 || ny >= M) continue;
			if (box[nx][ny] == 0) {
				box[nx][ny] = 1;
				days[cday + 1].push_back(make_pair(nx, ny));
				cnt++;
			}
		}
	}
	// 익은 토마토가 없다면 false 반환
	if (cnt == 0) return 0;
	return cday+1;
}

int tomatoProc() {
	int cday = 0, ret = 0, tmp = 0;
	while (true) {
		if ((tmp = spendDay(cday++)) == 0) break;
		ret = tmp;
	}

	// 익지 않은 토마토가 있다면 -1 반환
	for (int r = 0; r < N; r++) {
		for (int c = 0; c < M; c++) {
			if (box[r][c] == 0) return -1;
		}
	}
	return ret;
}


int main()
{
	cin.tie(NULL);
	ios_base::sync_with_stdio(false);
	scanf("%d %d", &M, &N);

	int val = 0;
	for (int r = 0; r < N; r++) {
		for (int c = 0; c < M; c++) {
			scanf("%d", &val);
			box[r][c] = val;
			if (val == 1) days[0].push_back(make_pair(r, c));
		}
	}

	int ans = tomatoProc();
	printf("%d\n", ans);
	return 0;
}

Leave a comment