컴퓨터기본/문제풀이

[백준] 15684번: 사다리 조작

차가운오미자 2021. 11. 24. 23:08

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

 

15684번: 사다리 조작

사다리 게임은 N개의 세로선과 M개의 가로선으로 이루어져 있다. 인접한 세로선 사이에는 가로선을 놓을 수 있는데, 각각의 세로선마다 가로선을 놓을 수 있는 위치의 개수는 H이고, 모든 세로선

www.acmicpc.net

문제 이해

사다리에 가로선들을 추가해보는 모든 경우를 탐색한다.

하지만 사다리는 최대 3개만 놓을 수 있어서, 3개를 넘게 놓는 경우는 패스해준다.

0개, 1개, 2개, 3개 놓는 경우 중 조건에 해당하는 경우가 되면 그 때의 추가한 사다리 개수에 따라 min값을 갱신해주면 되는 문제이다.

 

map[i][j]는 j번 사다리에 j+1번으로 가는 (= 오른쪽으로 가는) 높이 i에 있는 사다리가 있다는 뜻이다.

그렇다는 건 j+1번 사다리에는 j번으로 가는 (=왼쪽으로 가는) 높이 i에 있는 사다리가 있다는 뜻이다. 

 

Check()

이 규칙을 이용해서 a번 사다리에서 a번 사다리로 가는지를 확인하는 것은 쉽다.

일단 처음에 현재위치 (cur)을 a로 잡아둔다.

위에서부터 내려가면서 cur 위치를 바꿔주면 된다.

map[a][h] 가 1이면 cur += 1 해주고, map[a-1][h] == 1이면 cur -= 1해주면 된다.

끝까지 내려왔을 때 cur == a이면 원래 위치로 돌아온 거니까 continue, 아니면 이 경우는 불가능한 경우이므로 return 0해주면 된다. 

int Check(void) {
	// 현재 맵의 상태에서 모든 시작점 == 도착점인지
	for (int i = 1; i <= N; i++) {
		int cur = i;
		for (int j = 1; j <= H; j++) {
			if (map[j][cur] != 0) cur += 1;
			else if (map[j][cur - 1] != 0) cur -= 1;
		}
		if (cur != i) return 0;
	}
	return 1;
}

 

DFS()

<재귀 부분>

DFS가 조금 헷갈리는데 모든 사다리들에 한 번씩 추가해보는 작업을 해야해서 이중 for문을 이용한다.

현재 위치에 사다리가 있는 거면 그냥 넘어가고, 

없는 상태면 한번 넣어보고 다음 DFS를 했다가, 돌아와서 다시 사다리를 없애준다.

 

<조건 확인 부분>

0개, 1개, 2개, 3개를 추가한 상태에서 모두 지금 사다리 상태가 조건에 부합하는지 (i번 사다리의 도착점은 i번) 를 확인해야 하므로, 항상 Check()를 해준다.

 

<종료 부분>

만약 3개 이상 추가한 경우라던가, 지금까지의 min_added값보다 많이 추가한 경우는 return해서 가지치기 해준다.

void DFS(int idx, int added) {

	// 종료
	if (added > 3 || added > min_added) {
		return;
	}

	if (Check()) {// 현재 맵의 상태에서 사다리 성공인지
		if (added < min_added) min_added = added;
		return;
	}

	for (int i = idx; i <= H; i++) {
		for (int j = 1; j < N; j++) {
			if (map[i][j] != 0) continue;
			if (map[i][j - 1] != 0) continue;
			if (map[i][j + 1] != 0) continue;

			map[i][j] = M + 1 + added;
			DFS(i, added + 1);
			map[i][j] = 0;
		}
	}
}

 

작성 코드

#include <stdio.h>
#include <string.h>
#define MAXN 10
#define MAXM 9*30
#define MAXH 30

int N, M, H; // 세로선, 기존 가로선 개수, 가로선
int map[MAXH + 5][MAXN + 5];
int min_added = 0x7fffffff;

void Get_Input(void) {
	int a, b;
	scanf("%d %d %d", &N, &M, &H);
	for (int i = 1; i <= M; i++) {
		scanf("%d %d", &a, &b);
		map[a][b] = i; // b, b+1 번 사이에 a번째 가로선이 있다
	}
}

int Check(void) {
	// 현재 맵의 상태에서 모든 시작점 == 도착점인지
	for (int i = 1; i <= N; i++) {
		int cur = i;
		for (int j = 1; j <= H; j++) {
			if (map[j][cur] != 0) cur += 1;
			else if (map[j][cur - 1] != 0) cur -= 1;
		}
		if (cur != i) return 0;
	}
	return 1;
}

void DFS(int idx, int added) {

	// 종료
	if (added > 3 || added > min_added) {
		return;
	}

	if (Check()) {// 현재 맵의 상태에서 사다리 성공인지
		if (added < min_added) min_added = added;
		return;
	}

	for (int i = idx; i <= H; i++) {
		for (int j = 1; j < N; j++) {
			if (map[i][j] != 0) continue;
			if (map[i][j - 1] != 0) continue;
			if (map[i][j + 1] != 0) continue;

			map[i][j] = M + 1 + added;
			DFS(i, added + 1);
			map[i][j] = 0;
		}
	}
}


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

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

[백준] 14891번: 톱니바퀴  (0) 2021.11.24
[백준] 15683번: 감시  (0) 2021.11.24
[백준] 14889번: 스타트와 링크  (0) 2021.11.24
[백준] 15685번: 드래곤 커브  (0) 2021.11.23
[백준] 15686번: 치킨 배달  (0) 2021.11.22