컴퓨터기본/문제풀이

[백준] 17136번: 색종이 붙이기

차가운오미자 2021. 11. 28. 17:00

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

 

17136번: 색종이 붙이기

<그림 1>과 같이 정사각형 모양을 한 다섯 종류의 색종이가 있다. 색종이의 크기는 1×1, 2×2, 3×3, 4×4, 5×5로 총 다섯 종류가 있으며, 각 종류의 색종이는 5개씩 가지고 있다. <그림 1> 색종이를 크

www.acmicpc.net

 

문제 이해

처음에는 그냥 단순히 하나하나 가능한 최대 크기의 색종이를 붙이는 식으로 했더니 예제는 다 통과되는데 내자마자 틀렸습니다가 떴다. 여기에 대한 반례가 하나 있는데

 

이렇게 되어 있으면 왼쪽 상단부터 시작을 하기 때문에

이렇게 채워진다

근데 사실 정답은

이거다, 그래서 각 좌표별로 붙일 수 있는 최대 크기의 색종이를 그냥 지정하면 안되고, 1인 경우에 사이즈1부터 5까지의 색종이들을 다 한번씩 넣어봐야 한다.

즉, DFS로 문제를 풀어야 하는 것이다.

 

이 DFS가 조금 헷갈렸는데 요점만 설명하자면

1. 종료 조건

  • 만약 min 색종이 개수보다 현재까지 붙인 색종이 수가 같거나 많으면 가볼 필요 없는 경우이므로 return한다
  • 만약 모든 칸에 색종이를 붙인 경우라면 return 한다. --> 이거 안해주면 만약 (9,9)가 1인 경우 색종이를 붙이고 min값에 비교를 못하게 된다.

2. 재귀

  • 색종이를 붙일 다음 위치를 찾는다. 전에 붙인 위치는 인자(y, x)로 받았으니까 거기서부터 1인 곳을 찾아주면 된다.
  • for (; i < 10; i++) {
      for (; j < 10; j++) {
        if (map[i][j] == 1) {
          find = 1;
          break;
        }
        if (i >= 9 && j >= 9) {
          if (cnt < min) min = cnt;
          return;
        }
      }
      if (find == 1) break;
      j = 0;
    }
  • 찾았으면 그 위치에 색종이를 붙여야 한다. 사이즈 5부터 1까지의 색종이를 붙이는 모든 경우의 수를 봐야한다.
  • 그 사이즈이 색종이가 안들어가면 continue, 그 사이즈 색종이가 더이상 안남았으면 continue한다
  • 그 크기의 색종이가 이 칸에 들어간다면 붙이고(Cover()), 남은 붙여야 하는 칸 개수(pcnt)를 줄이고, 남은 색종이 개수 배열(paper)도 줄여준다 다음 색종이 붙일 위치를 찾아 DFS를 다시 call한다
  • DFS에서 돌아왔으면 맵(Uncover())과 남은 붙여야하는 칸의 개수(pcnt), 남은 색종이 개수 배열(paper)를 복구해준다.
  • for (int k = 5; k >= 1; k--) {
      if (!Check(i, j, k)) continue; // 이 크기 못붙임
      if (paper[k] <= 0) continue; // 색종이 안남아있음 -> 못붙임
      Cover(i, j, k);
      pcnt -= k * k;
      paper[k]--;
      DFS(i, j + 1, cnt + 1); // 다음 위치부터 탐색
      paper[k]++;
      pcnt += k * k;
      Uncover(i, j, k);
    }

 

전체 코드

#include <stdio.h>
#include <string.h>
#define INF 0x7fffffff

int map[10 + 2][10 + 2];
int paper[5+2] = { 0, 5, 5, 5, 5, 5 };
int min = INF;
int pcnt;

void Get_Input(void) {
	for (int i = 0; i < 10; i++) {
		for (int j = 0; j < 10; j++) {
			scanf("%d", &map[i][j]);
			if (map[i][j] == 1) pcnt++;
		}
	}
}

int Check(int y, int x, int len) {
	for (int i = 0; i < len; i++) {
		for (int j = 0; j < len; j++) {
			if (map[y+i][x+j] != 1) return 0;
		}
	}
	return 1;
}

void Cover(int y, int x, int len) {
	for (int i = 0; i < len; i++) {
		for (int j = 0; j < len; j++) {
			map[y+i][x+j] = (len+10);
		}
	}
}

void Uncover(int y, int x, int len) {
	for (int i = 0; i < len; i++) {
		for (int j = 0; j < len; j++) {
			map[y + i][x + j] = 1;
		}
	}
}

void Print_Map(void) {
	for (int i = 0; i < 10; i++) {
		for (int j = 0; j < 10; j++) {
			printf("%2d ", map[i][j]);
		} 
		printf("\n");
	}
	printf("\n");
}

void DFS(int y, int x, int cnt) {
	int i = y, j = x;
	int find = 0;
	if (cnt >= min) return;
	if (pcnt == 0) {
		if (cnt < min) min = cnt;
		return;
	}
	// 재귀
	// 1. 색종이 붙일 다음 위치 찾기
	for (; i < 10; i++) {
		for (; j < 10; j++) {
			if (map[i][j] == 1) {
				find = 1;
				break;
			}
			if (i >= 9 && j >= 9) {
				if (cnt < min) min = cnt;
				return;
			}
		}
		if (find == 1) break;
		j = 0;
	}

	for (int k = 5; k >= 1; k--) {
		if (!Check(i, j, k)) continue; // 이 크기 못붙임
		if (paper[k] <= 0) continue; // 색종이 안남아있음 -> 못붙임
		Cover(i, j, k);
		pcnt -= k * k;
		paper[k]--;
		DFS(i, j + 1, cnt + 1); // 다음 위치부터 탐색
		paper[k]++;
		pcnt += k * k;
		Uncover(i, j, k);
	}

}

int main(void) {
	Get_Input();
	DFS(0, 0, 0);
	if (min == INF) printf("-1");
	else printf("%d", min);
	return 0;
}

'컴퓨터기본 > 문제풀이' 카테고리의 다른 글

[백준] 17070번: 파이프 옮기기 1  (0) 2021.11.28
[백준] 17135번: 캐슬 디펜스  (0) 2021.11.28
[백준] 12100번: 2048(Easy)  (0) 2021.11.26
[백준] 3190번: 뱀  (0) 2021.11.26
[백준] 14499번: 주사위 굴리기  (0) 2021.11.26